Example #1
0
    def __init__(self, name: str, data: dict):
        self.sound_name = name
        self.sound_data = data

        files = data["files"]

        arrays, weights = [], []
        for fd in files:
            filenames = []
            if "glob" in fd.keys():
                filenames.extend(glob.glob(fd["glob"]))
            if "filenames" in fd.keys():
                filenames.extend(fd["filenames"])
            if "filename" in fd.keys():
                filenames.append(fd["filename"])

            if len(filenames) >= 1:
                arrays.append(filenames)
                weights.append(fd.get("weight", 1))

        self.sound_arrays = arrays
        self.sound_weights = weights

        commands.Command.__init__(self,
                                  soundplayer_callback,
                                  name=name,
                                  aliases=data.get("aliases", []),
                                  **data.get("commandkwargs", {}))

        commands.guild_only()(
            self)  # make the command guild-only by adding the check
Example #2
0
 async def cog_check(self, ctx):
     return all([
         await check.predicate(ctx) for check in {
             commands.guild_only(),
             commands.bot_has_guild_permissions(manage_roles=True)
         }
     ])
Example #3
0
 def __init__(self, debug=False, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.debug = debug
     self.logger = Logger(name, log_level, log_type)
     self.logger.info(f"Starting {name}")
     self.cfg = ConfigUtil()
     self.add_check(commands.guild_only())
Example #4
0
def main():
    bot = commands.Bot(command_prefix=commands.when_mentioned_or('!'),)

    async def on_ready():
        logger.info(f'{bot.user} has connected to Discord!')

    bot.add_listener(on_ready)
    bot.add_check(commands.guild_only())
    # bot.add_cog(BGGCog(bot))
    bot.add_cog(Music(bot))
    bot.run(token)
Example #5
0
class Roleplay(Cog):
    """Commands related to roleplay."""

    def __init__(self, bot):
        super().__init__(bot)

    @commands.command(name='rmember', aliases=['rmem', 'draw'])
    @commands.check(commands.guild_only())
    async def rand_member(self, ctx):
        """Choose a random member from the message's guild."""
        satisfied = False
        m_list = list(ctx.guild.members)
        while not satisfied:
            rmem = random.choice(m_list)
            satisfied = str(rmem.status) == 'online'
        await ctx.send(rmem.mention)
        return rmem

    @commands.command(aliases=['boop', 'poke', 'hit'])
    async def slap(self, ctx, *, target: str):
        """Slap someone, for the win.
        Usage: slap [person]"""
        keystr = '* ' + ctx.message.content.split()[0][len(ctx.prefix):] + 's *'
        await ctx.send('*' + ctx.author.display_name + keystr +
                           target + '* **' + random.choice(adjs) + '**.')

    @commands.command(aliases=['stab', 'kill', 'punch', 'shoot', 'hurt', 'fight'])
    async def attack(self, ctx, *, target: str):
        """Hurt someone with determination in the shot.
        Usage: attack [person]"""
        await ctx.send('*' + ctx.author.display_name + '* ' +
                           random.choice(fights).format('*' + target + '*') + '. '
                           + random.choice(death).format('*' + target + '*'))

    @commands.command()
    async def charlie(self, ctx, *, question: str):
        """Ask a question... Charlie Charlie are you there?
        Usage: charlie [question to ask, without punctuation]"""
        aq = '' if question.endswith('?') else '?'
        await ctx.send('*Charlie Charlie* ' + question + aq + "\n**" +
                           random.choice(['Yes', 'No']) + '**')

    @commands.command(aliases=['soontm'])
    async def soon(self, ctx):
        """Feel the loading of 10000 years, aka Soon™.
        Usage: soon"""
        e = discord.Embed(color=random.randint(1, 255**3-1))
        e.set_image(url='https://images.discordapp.net/.eJwFwdENhCAMANBdGIBiK2dxG4KIJGoN7X1dbnff-7nvON3qDrNHV4Cta5GxeTUZuVXfRNpZ89PVF7kgm-VyXPU2BYwYF6Y0cwgTcsAJMOFMxJESBqblQwgqcvvWd_d_AZ09IXY.TT-FWSP4uhuVeunhP1U44KnCPac')
        await ctx.send(embed=e)
Example #6
0
def is_moderator():
    """A check that determins if a user is a moderator

    This is similar to `is_guild_moderator` except it uses `has_permissions` instead of `has_guild_permissions`
    """

    guild_only = commands.guild_only().predicate
    perms = commands.has_permissions(manage_messages=True,
                                     kick_members=True,
                                     ban_members=True).predicate

    async def predicate(ctx: Context) -> bool:
        return await guild_only(ctx) and await perms(ctx)

    return commands.check(predicate)
Example #7
0
def sensitive():
    check_guild = commands.guild_only().predicate
    has_bypass = commands.has_guild_permissions(manage_messages=True).predicate

    async def extended_check(ctx):
        await check_guild(ctx)

        sensitive = await ctx.bot.get_cog("Config").get_value(
            ctx.guild, get(config.configurables, name="private")
        )

        if sensitive:
            return await has_bypass(ctx)

        return True

    return commands.check(extended_check)
Example #8
0
def is_guild_moderator():
    """A check that determines if a user is a guild moderator

    This is done by checking if the user has the following guild permissions:
    - `Manage Messages`
    - `Kick Members`
    - `Ban Members`
    """

    guild_only = commands.guild_only().predicate
    perms = commands.has_guild_permissions(manage_messages=True,
                                           kick_members=True,
                                           ban_members=True).predicate

    async def predicate(ctx: Context) -> bool:
        return await guild_only(ctx) and await perms(ctx)

    return commands.check(predicate)
Example #9
0
class Ranks(Cog):
    """Ranks and levels."""
    async def on_not_command(self, msg):
        """Do level-up logic."""
        if self.bot.selfbot: return
        if isinstance(msg.channel, discord.abc.PrivateChannel): return
        if msg.author.bot: return
        prof_name = 'profile_' + str(msg.guild.id)
        prof = self.bot.store.get_prop(msg, prof_name)
        prof['exp'] += math.ceil(((len(msg.content) / 6) * 1.5) +
                                 random.randint(0, 14))
        new_level = rank.xp_level(prof['exp'])[0]
        if new_level > prof['level']:
            bclu = self.bot.store.get_prop(msg, 'broadcast_level_up')
            if isinstance(bclu, str):
                bclu = bclu.lower()
            if bclu in bool_true:
                await msg.channel.send(
                    '**Hooray!** {0.mention} has just *advanced* to **level {1}**.'
                    .format(msg.author, str(new_level)))
        prof['level'] = new_level
        self.bot.store.set_prop(msg, 'by_user', prof_name, prof)

    @commands.command(aliases=['xp', 'level', 'lvl', 'exp', 'levels'])
    @commands.check(commands.guild_only())
    async def rank(self, ctx, *, user: discord.Member):
        """Check experience, level, and rank!
        Usage: xp {user}"""
        stat_fmt = '''{0.author.mention} Here are {5} **stats**:
**LEVEL: {1}
EXPERIENCE: __{2}/{3}__ for next level
TOTAL EXPERIENCE: {4}**
*Try getting some more! :smiley:*
'''
        target = FakeMessageMember(user)
        prof = self.bot.store.get_prop(target,
                                       'profile_' + str(target.guild.id))
        rlevel = rank.xp_level(prof['exp'])
        await ctx.send(
            stat_fmt.format(target, str(rlevel[0]), str(int(rlevel[1])),
                            str(int((rlevel[0] + 1) * lvl_base)),
                            str(prof['exp']),
                            ('your' if target.author.id == ctx.author.id else
                             str(target.author) + "'s")))
Example #10
0
async def on_ready():
    print("{0.name} is ready!\nID: {0.id}\ndiscord.py Version: {1}".format(
        client.user, str(discord.__version__)))
    await client.change_presence(activity=UpdatedPresence(client))

    # Cogs and Checks (only on first start)
    global starting
    if starting:
        global to_cog
        global stats_cog
        global debug_cog
        starting = False
        to_cog = TOrganizer(client)
        stats_cog = Stats(client)
        debug_cog = Debug(client)
        misc_cog = Misc(client)

        client.add_check(commands.guild_only())
        client.add_cog(to_cog)
        client.add_cog(stats_cog)
        client.add_cog(debug_cog)
        client.add_cog(misc_cog)
Example #11
0
    def register_commands(self, bot: commands.Bot):
        @bot.group(
            name='duel',
            description='Command for managing duels',
        )
        @commands.check(commands.guild_only())
        async def duel(ctx: commands.Context):
            if not ctx.invoked_subcommand:
                ctx.channel.send('Invalid Subcommand!')

        @duel.command(
            name='challenge',
            description='Challenges another user',
        )
        async def challenge(ctx: commands.Context):
            raise NotImplementedError  # TODO

        @duel.command()
        async def list_duels(ctx: commands.Context):
            raise NotImplementedError  # TODO

        @duel.command()
        async def duel_params(ctx: commands.context):
            raise NotImplementedError  # TODO
    'separate': False,
}

logger = logging.getLogger('logs_uploader')
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
logger.addHandler(handler)

# Don't post to team channels and force the guild used so testing can you DMs
DISCORD_TESTING = bool(os.getenv('DISCORD_TESTING'))
# Just post all messages to calling channel, allow DMs
DISCORD_DEBUG = bool(os.getenv('DISCORD_DEBUG'))
if DISCORD_TESTING or DISCORD_DEBUG:
    # Allow DMs in testing
    guild_only = commands.check_any(commands.guild_only(),
                                    commands.dm_only())  # type: ignore
    # print all debug messages
    logger.setLevel(logging.DEBUG)
    handler.setLevel(logging.DEBUG)
else:
    guild_only = commands.guild_only()

bot = commands.Bot(command_prefix='!')


async def log_and_reply(ctx: commands.Context, error_str: str) -> None:
    logger.error(error_str)
    await ctx.reply(error_str)

Example #13
0
 def cog_check(self, ctx):
     return commands.guild_only().predicate(ctx)
Example #14
0
 async def __local_check(self, ctx):
     return commands.guild_only()
Example #15
0
 def __init__(self, bot):
     super().__init__(bot)
     for cmd in self.get_commands():
         cmd.add_check(commands.guild_only())
Example #16
0
import emoji
from discord.ext import commands

from dog import Cog
from dog.core import checks, converters
from dog.core.checks import create_stack
from dog.core.context import DogbotContext
from dog.core.converters import DeleteDays
from dog.core.utils.formatting import describe

logger = logging.getLogger(__name__)
CUSTOM_EMOJI_REGEX = re.compile(r'<:([a-zA-Z_0-9-]+):(\d+)>')
purge_command = create_stack(
    checks.is_moderator(),
    checks.bot_perms(manage_messages=True, read_message_history=True),
    commands.guild_only())


def _create_purge_command(name,
                          base_purge_handler,
                          *,
                          parent,
                          aliases=None,
                          help_text=''):
    """A function used to dynamically construct purge command subcommands."""
    @parent.command(name=name, aliases=aliases or [])
    @purge_command
    async def _purge_generated(self, ctx, amount: int = 5):
        """..."""
        await self.base_purge(ctx, amount, base_purge_handler)
Example #17
0
 def __init__(nested_self, bot):
     super().__init__(bot)
     if guild_only:
         nested_self.settings.add_check(commands.guild_only().predicate)
Example #18
0
 def wrapper(func):
     # wooo, currying
     return commands.guild_only()(commands.command(*args, **kwargs)(func))
Example #19
0
 def __init__(self, bot):
     super().__init__(bot)
     for cmd in self.get_commands():
         cmd.add_check(commands.guild_only())
         if cmd.name in ['서버정보', '권한']:
             cmd.add_check(partial(self.check.subcmd_valid, True))
Example #20
0
class Admin:
    """
    Administrative tasks.
    """
    def __init__(self):
        # Might be nicer to also have "@botuser get invite" as a command if
        # "@botuser add me" is not intuitive enough for me to remember.
        self.add_group.command(name='me',
                               brief='Gets an invite URL.')(self.add_guild)
        self.get_group.command(name='invite',
                               brief='Gets an invite URL.')(self.add_guild)

        self.remove_group.command(name='guild',
                                  aliases=['guilds', 'server', 'servers'],
                                  brief='Makes me leave a given server.')(
                                      self.remove_guild)
        commands.command(name='leave', brief='Makes me leave a given server.')(
            self.remove_guild)

    @staticmethod
    async def __local_check(ctx):
        return (await ctx.bot.is_owner(ctx.author)
                or ctx.author.id in ctx.bot.commanders)

    @util.group(name='get', aliases=['list'], brief='Lists various elements.')
    async def get_group(self, ctx):
        pass

    @get_group.command(name='commanders',
                       aliases=['commander'],
                       brief='Lists the current commanders for my bot.')
    async def get_commanders(self, ctx):
        """Lists any commanders authorized to use me."""
        embed = discord.Embed(
            title=f'Authorized commanders for {ctx.bot.user} are',
            color=0x00FFFF,
        )

        embed.set_thumbnail(url=ctx.bot.user.avatar_url)

        user_strings = []

        for id in ctx.bot.commanders:
            # Use the cached version or we will be dead before the
            # command finishes execution.
            user = ctx.bot.get_user(id)

            if ctx.guild is not None and user in ctx.guild.members:
                user_strings.append(f'- {user.mention} `{user.id}`')
            else:
                user_strings.append(f'- {user} `{user.id}`')

        embed.description = '\n'.join(user_strings)
        msg = await ctx.send(embed=embed)
        util.confirm_operation(ctx)
        util.make_closeable(ctx, msg)

    @get_group.command(name='guilds',
                       aliases=['guild', 'server', 'servers'],
                       brief='Lists the guilds I am part of.')
    async def get_guilds(self, ctx):
        """Lists all guilds I am currently connected to."""
        embed = discord.Embed(title=f'{ctx.bot.user} is currently a member in',
                              color=0xFFFF00)

        for guild in ctx.bot.guilds:
            d = [
                f'**ID**: `{guild.id}`',
                f'**Text Channels**: {len(guild.text_channels)}',
                f'**Voice Channels**: {len(guild.voice_channels)}',
                f'**Categories**: {len(guild.categories)}',
                f'**Members**: {len(guild.members)}',
                f'**MFA**: {"2FA" if guild.mfa_level else "None"}',
                f'**Verif\'n Level**: {guild.verification_level}',
                f'**Content Filter**: {guild.explicit_content_filter}',
                f'**Owner**: {guild.owner} `{guild.owner.id}`'
            ]

            if len(embed.fields) == 0:
                embed.set_thumbnail(url=guild.icon_url)

            embed.add_field(name=guild.name, value='\n'.join(d), inline=False)

        msg = await ctx.send(embed=embed)
        util.confirm_operation(ctx)
        util.make_closeable(ctx, msg)

    @commands.check(commands.guild_only())
    @get_group.command(
        name='perms',
        aliases=['perm', 'permission', 'permissions'],
        brief='Lists the permissions for this guild that I am granted.',
    )
    async def get_permissions(self, ctx):
        """Lists the current permissions the bot has in the current guild."""

        perms = ctx.guild.me.guild_permissions
        truthy_perms = [p[0] for p in perms if p[1]]
        falsey_perms = [p[0] for p in perms if not p[1]]

        truthy_perms = '\n'.join(map(str, truthy_perms))
        falsey_perms = '\n'.join(map(str, falsey_perms))

        embed = discord.Embed(title=f'My permissions in {ctx.guild}',
                              color=0xFF00FF)

        embed.set_thumbnail(url=ctx.guild.icon_url)

        embed.add_field(name='Permissions granted',
                        value=truthy_perms if truthy_perms else 'None')
        embed.add_field(name='Permissions denied',
                        value=falsey_perms if falsey_perms else 'None')

        msg = await ctx.send(embed=embed)
        util.confirm_operation(ctx)
        util.make_closeable(ctx, msg)

    @get_group.command(
        name='emojis',
        aliases=[
            'emoji', 'emote', 'emotes', 'emoticon', 'reaction'
            'emoticons', 'react', 'reacts', 'reactions'
        ],
        brief='Lists the emojis that I will currently react with.')
    async def get_emojis(self, ctx):
        embed = discord.Embed(
            title=f'My super dank list of emojis to shitpost with',
            color=0xFFAACC)

        embed.set_thumbnail(url=ctx.bot.user.avatar_url)
        emojis = ctx.bot.loaded_emojis
        embed.description = '\n'.join(
            map(lambda e: f'{str(e)} {e.name} {e.id}', emojis))

        msg = await ctx.send(embed=embed)
        util.confirm_operation(ctx)
        util.make_closeable(ctx, msg)

    @get_group.command(name='blacklist',
                       brief='Gets the blacklisted channels to not react in.')
    async def get_blacklist(self, ctx):
        """
        This blacklist exists to ensure that the bot does not react in
        inappropriate situations.
        """
        blacklist = ctx.bot.dont_react_in.get()

        embed = discord.Embed(
            title='Channels I am not allowed to react in',
            description='These are the channels I am not allowed to react to '
            'messages in...',
            color=0xB000B5)

        # Guild is a string. JSON standard says we can't use ints as keys.
        # Bloody stupid.
        for guild, channels in blacklist.items():
            guild_obj = discord.utils.find(lambda g: str(g.id) == guild,
                                           ctx.bot.guilds)

            if guild_obj is None:
                continue

            channel_objs = []
            for channel in channels:
                chan_obj = discord.utils.find(lambda c: c.id == channel,
                                              guild_obj.text_channels)

                if chan_obj is None:
                    continue

                channel_objs.append(chan_obj)

            embed.add_field(name=guild_obj.name,
                            value='\n'.join(
                                map(lambda c: f'#{c.name}', channel_objs)),
                            inline=False)

        if len(embed.fields) == 0:
            embed.add_field(name='I am a free bot.',
                            value='Nothing is blacklisted!')

        msg = await ctx.send(embed=embed)
        util.confirm_operation(ctx)
        util.make_closeable(ctx, msg)

    @get_group.command(
        name='trigger',
        aliases=[
            'triggers', 'word', 'words', 'regex', 'regexes', 'regexs',
            'pattern', 'patterns', 'phrase', 'phrases'
        ],
        brief='Lists all phrases/regular expressions I understand.')
    async def get_phrases(self, ctx):
        # noinspection PyProtectedMember
        patterns = []

        for pattern in ctx.bot.patterns:
            # Determines if the regex was probably added as a word.
            match = re.match(r'\\b\(([^)]+)\)\\b', pattern.pattern)
            if match:
                patterns.append(match.group(1))
            else:
                patterns.append(f'`{pattern.pattern}` (regex)')

        embed = discord.Embed(title=f'Things I react to',
                              description='\n'.join(patterns),
                              color=0xFEC)
        embed.set_thumbnail(url=ctx.bot.user.avatar_url)

        msg = await ctx.send(embed=embed)
        util.confirm_operation(ctx)
        util.make_closeable(ctx, msg)

    """
    'Add' commands
    """

    @util.group(name='add', brief='Adds various elements.')
    async def add_group(self, ctx):
        pass

    # Apparently making a staticmethod shits across the resolution of coro-s
    # ... thanks Guido.
    # noinspection PyMethodMayBeStatic
    async def add_guild(self, ctx, *, user: commands.MemberConverter = None):
        """
        DM's you the invite to the server. If you mention someone, then they
        get sent a URL instead of you.
        """
        if user is None:
            user = ctx.author
        await user.send(f'Add me to your server, please.\n\n{ctx.bot.invite}')
        util.confirm_operation(ctx)

    # noinspection PyUnresolvedReferences
    @commands.check(commands.guild_only())
    @add_group.command(name='commander',
                       brief='Authorizes a user to talk on my behalf.')
    async def add_commander(self, ctx, *, commander: commands.MemberConverter):
        commanders = ctx.bot.commanders.get()

        if commander == ctx.bot.user:
            raise NameError('I am not going to command _myself_...')
        elif commander.bot:
            raise NameError(
                'I am not going to be controlled by a lowlife bot!')
        elif commander.id in commanders:
            raise NameError('That name is already in my list. Tough luck.')
        else:
            commanders.append(commander.id)
            # Commit to disk
            await ctx.bot.commanders.set(commanders)
            util.confirm_operation(ctx)

    @commands.check(commands.guild_only())
    @add_group.command(name='emoji',
                       aliases=['emote', 'emoticon', 'reaction', 'react'],
                       brief='Adds a new emoji to my reaction list.')
    async def add_emoji(self, ctx, *, emote: commands.EmojiConverter):
        emotes = ctx.bot.loaded_emojis
        if emote not in emotes:
            emotes.append(emote)
            await ctx.bot.set_emoji_list(emotes)
            util.confirm_operation(ctx)
        else:
            raise ValueError(f'Emote already exists in my list. {emote}')

    @add_group.command(name='regex',
                       aliases=['pattern'],
                       brief='Adds a regular expression to Kat. Ask Espy.')
    async def add_pattern(self, ctx, *, phrase: str):
        """Compiles the given regular expression."""
        regex = re.compile(phrase, flags=re.I)
        # Will assert that this compiled? Maybe?
        regex.match('')
        patterns = ctx.bot.patterns
        patterns.append(regex)
        await ctx.bot.set_pattern_list(patterns)
        util.confirm_operation(ctx)

    @add_group.command(
        name='word',
        aliases=['phrase'],
        brief='Adds a word or phrase to the list of things I can react to.',
    )
    async def add_word(self, ctx, *, phrase: str):
        """Compiles the given phrase into a regular expression."""
        # Hacky, but if I put brackets around said phrase... it should make
        # sure I detect that this is a command added word, then in the list
        # we don't have to show it in backticks. Hacky work around. Sue me, etc.
        regex = re.compile('\\b(%s)\\b' % phrase, flags=re.I | re.U | re.M)
        # Will assert that this compiled? Maybe?
        patterns = ctx.bot.patterns
        patterns.append(regex)
        await ctx.bot.set_pattern_list(patterns)
        util.confirm_operation(ctx)

    @commands.check(commands.guild_only())
    @add_group.command(
        name='blacklist',
        brief='Blacklists a given channel, or if unspecified, __this__ channel.'
    )
    async def add_blacklist(self,
                            ctx,
                            *,
                            channel: commands.TextChannelConverter = None):
        if channel is None:
            channel = ctx.channel

        guild_id = str(ctx.guild.id)
        chan_id = channel.id

        blacklist = ctx.bot.dont_react_in.get()

        if guild_id in blacklist:
            if chan_id in blacklist[guild_id]:
                raise ValueError(f'{channel.name} is already blacklisted on '
                                 'this server.')
            else:
                blacklist[guild_id].append(chan_id)
        else:
            blacklist[guild_id] = [chan_id]

        await ctx.bot.dont_react_in.set(blacklist)
        util.confirm_operation(ctx)

    """
    'Remove' methods.
    """

    @util.group(name='remove', brief='Removes various elements.')
    async def remove_group(self, ctx):
        pass

    # noinspection PyMethodMayBeStatic
    async def remove_guild(self, ctx, *, guild):
        """
        Removes my user from a given server.
        """
        # Try to find the guild.
        guild_obj = discord.utils.find(
            lambda g: g.name.lower() == guild.lower() or g.id == guild,
            ctx.bot.guilds)

        if guild_obj is None:
            raise ValueError('Could not find a guild with that name or ID.')
        else:
            if ctx.guild == guild_obj:
                # We can't react after we have left.
                await ctx.message.add_reaction('\N{OK HAND SIGN}')
                await guild_obj.leave()
            else:
                await guild_obj.leave()
                util.confirm_operation(ctx)

    @remove_group.command(
        name='commander',
        aliases=['commanders'],
        brief='De-authenticates a given commander from using me.')
    async def remove_commander(self, ctx, *, member: commands.MemberConverter):
        """Removes a commander from my authenticated list."""
        # Don't allow removal of the bot owner.
        if await ctx.bot.is_owner(member):
            raise PermissionError('You cannot remove the bot owner.')
        else:
            commanders = ctx.bot.commanders.get()
            commanders.remove(member.id)
            await ctx.bot.commanders.set(commanders)
            util.confirm_operation(ctx)

    @remove_group.command(name='regex',
                          aliases=['pattern', 'word', 'phrase'],
                          brief='Removes a given regex or phrase.')
    async def remove_pattern(self, ctx, *, pattern):
        """
        Removes the given pattern from the list of patterns I react to.
        """
        regex = discord.utils.find(lambda r: r.pattern == pattern,
                                   ctx.bot.patterns)

        if regex is None:
            regex = discord.utils.find(
                lambda r: r.pattern.lower() == f'\\b({pattern.lower()})\\b',
                ctx.bot.patterns)

        if regex is None:
            raise ValueError('Could not find that pattern.')
        else:
            patterns = ctx.bot.patterns
            patterns.remove(regex)
            await ctx.bot.set_pattern_list(patterns)
            util.confirm_operation(ctx)

    @remove_group.command(name='emoji',
                          aliases=['emote'],
                          brief='Removes an emote from my list of reactions.')
    async def remove_emoji(self, ctx, *, emoji: commands.EmojiConverter):
        """Removes the emoji from my reaction list, if it is there."""
        emojis = ctx.bot.loaded_emojis
        if emoji not in emojis:
            raise ValueError('That emoji is not registered.')
        else:
            emojis.remove(emoji)
            await ctx.bot.set_emoji_list(emojis)
            util.confirm_operation(ctx)

    @commands.check(commands.guild_only())
    @remove_group.command(
        name='blacklist',
        brief='Un-blacklists a given channel, or if unspecified, '
        '__this__ channel.')
    async def remove_blacklist(self,
                               ctx,
                               *,
                               channel: commands.TextChannelConverter = None):
        if channel is None:
            channel = ctx.channel

        guild_id = str(ctx.guild.id)
        chan_id = channel.id

        blacklist = ctx.bot.dont_react_in.get()

        if guild_id not in blacklist:
            if chan_id not in blacklist[guild_id]:
                raise ValueError(f'{channel.name} is not blacklisted on '
                                 'this server anyway.')
            else:
                blacklist[guild_id].remove(chan_id)
        else:
            blacklist.pop(guild_id)

        await ctx.bot.dont_react_in.set(blacklist)
        util.confirm_operation(ctx)

    @util.command(
        name='stop',
        aliases=['kill', 'reboot', 'restart', 'logout', 'exit', 'quit'],
        brief='Kills the bot process (Generally causes bot to restart).')
    async def stop(self, ctx):
        await ctx.send(
            random.choice([
                'Yessir!', 'Sure.', 'Do I really have to?', 'Goodbye ;-;',
                '*cries*...\n\n*sniffle* ...o..okay...',
                '*deletes the other bots', '*hisses*', '*nods*', 'kk', 'k',
                'Sure thing sauce boss.', 'Gimme a sec then!', 'Okydokes',
                'Ja.', 'PlEaSe ShUtDoWn FoR Me BoT!', 'Meh',
                'You are no fun...',
                'Soon. Soon you will regret the day you messed with --'
                '*powers down*'
            ]))
        await ctx.bot.logout()
Example #21
0
import discord
from ddtrace import tracer
from discord import app_commands
from discord.app_commands import Choice
from discord.ext import commands

from .. import SpellBot
from ..actions import ConfigAction
from ..metrics import add_span_context
from ..utils import for_all_callbacks

logger = logging.getLogger(__name__)


@for_all_callbacks(commands.guild_only())
class ConfigCog(commands.Cog):
    def __init__(self, bot: SpellBot):
        self.bot = bot

    @app_commands.command(
        name="power",
        description="Set your power level.",
    )
    @app_commands.describe(level="What is your current power level?")
    @app_commands.choices(
        level=[
            Choice(name="1", value=1),
            Choice(name="2", value=2),
            Choice(name="3", value=3),
            Choice(name="4", value=4),
Example #22
0
from settings import BOT_TOKEN, BOT_APPLICATION_ID, BOT_ROLES_DB, BOT_GUILD_IDS
import discord
from discord.ext import commands
from discord_slash import SlashCommand, SlashContext
from discord_slash.utils.manage_commands import create_option
import api
from itertools import dropwhile

intent = discord.Intents.default()
intent.members = True

bot = commands.Bot(command_prefix='prefix', intents=intent)
bot.add_check(commands.guild_only())
slash = SlashCommand(bot,
                     sync_commands=True,
                     application_id=BOT_APPLICATION_ID)

con, cur = api.makeApi(BOT_ROLES_DB)

LIMITS = {
    'content': 2000,
    'embeds': 10,
    'title': 256,
    'description': 2048,
    'author': 256,
    'fields': 25,
    'field_name': 256,
    'field_value': 1024,
    'footer': 2048,
    'embed': 6000
}
Example #23
0
 def cog_check(self, ctx: commands.Context):
     return commands.guild_only() and \
            Utils.is_loaded(self.qualified_name.lower(), ctx.guild.id) and \
            Utils.is_authorized(ctx.author, ctx.guild.id) and \
            not Utils.is_banned(ctx.command, ctx.author, ctx.guild.id)
Example #24
0
class Admin(Cog):
    """Commands useful for admins and/or moderators.
    Can be extremely powerful, use with caution!
    """
    @commands.command(aliases=['clear', 'nuke', 'prune', 'clean'])  # no_pm
    @commands.check(commands.guild_only())
    async def purge(self, ctx, *count):
        """Purge a channel of messages.
        Usage: purge"""
        if self.bot.selfbot:
            await ctx.send(
                '**This command doesn\'t work in selfbot mode, due to a Discord restriction.**'
            )
            return
        or_check_perms(ctx,
                       ['manage_guild', 'manage_channels', 'manage_messages'])
        mode = 'count'
        detected = False
        if not count:
            limit = 1500
            detected = True
        elif len(count) == 1:
            if count[0] == 'infinite':
                limit = 1600
                detected = True
            else:
                try:
                    limit = abs(int(count[0])) + 1
                    if limit > 1600:
                        await ctx.send(
                            ctx.mention +
                            ' **You can only clean messages by user or 1-1600!**'
                        )
                        return
                    detected = True
                except ValueError:
                    pass
        if not detected:
            mode = 'target'
            targets = set()
            members = {}
            s = ctx.guild
            for i in getattr(s, 'members', []):
                members[i.mention] = i
                members[i.id] = i
                members[i.display_name] = i
                members[i.name] = i
            for i in count:
                try:
                    member = s.get_member(i)
                except AttributeError:
                    try:
                        member = await self.bot.get_user_info(i)
                    except discord.HTTPException:
                        member = None
                if member:
                    targets.add(member)
                else:
                    try:
                        member = await self.bot.get_user_info(i)
                    except discord.HTTPException:
                        member = None
                    if member:
                        targets.add(member)
            names = []
            _i = 0
            while _i < len(count):
                names.append(count[_i])
                with suppress(KeyError):
                    if ' '.join(names) in members:
                        targets.add(members[' '.join(names)])
                        names = []
                    elif _i + 1 == len(count):
                        targets.add(members[count[0]])
                        _i = -1
                        users = count[1:]
                        names = []
                _i += 1
            if not targets:
                await ctx.send(
                    '**No matching users, try again! Name, nickname, name#0000 (discriminator), or ID work. Spaces do, too!**'
                )
                return
            purge_ids = [m.id for m in targets]
        try:
            if mode == 'count':
                deleted = await ctx.channel.purge(limit=limit)
            else:
                deleted = await ctx.channel.purge(
                    limit=1500, check=lambda m: m.author.id in purge_ids)
        except discord.Forbidden:
            await ctx.send(
                ctx.mention +
                ' **I don\'t have enough permissions to do that here 😢**')
            return
        except discord.HTTPException as e:
            if '14 days old' in str(e):
                await ctx.send(
                    'I can only purge messages under 14 days old :sob:')
                return
            else:
                raise e
        dn = len(deleted)
        del_msg = await ctx.send(
            '👏 I\'ve finished, deleting {0} message{1}!'.format(
                (dn if dn else 'no'), ('' if dn == 1 else 's')))
        await asyncio.sleep(2.8)
        await del_msg.delete(reason='Deleting "finished purging" message')

    @commands.command(aliases=['amiadmin', 'isadmin', 'admin'])
    async def admintest(self, ctx):
        """Check to see if you're registered as a bot admin.
        Usage: admintest'"""
        tmp = check_perms(ctx, ('bot_admin', ))
        if tmp:
            await ctx.send(ctx.mention + ' You are a bot admin! :smiley:')
        else:
            await ctx.send(ctx.mention +
                           ' You are not a bot admin! :slight_frown:')

    @commands.command(aliases=['adminadd'])
    async def addadmin(self, ctx, *rrtarget: str):
        """Add a user to the bot admin list.
        Usage: addadmin [user]"""
        tmp = check_perms(ctx, ('bot_admin', ))
        if not rrtarget:
            await ctx.send(
                '**You need to specify a name, nickname, name#0000, mention, or ID!**'
            )
            return
        rtarget = ' '.join(rrtarget)
        try:
            _target = ctx.guild.get_member_named(rtarget)
        except AttributeError:
            _target = None
        if _target:
            target = _target.id
        elif len(rtarget) == 18:
            target = rrtarget[0]
        elif ctx.message.mentions:
            target = ctx.message.mentions[0].id
        else:
            await ctx.send(
                '**Invalid name! Name, nickname, name#0000, mention, or ID work.**'
            )
            return
        if tmp:
            aentry = target
            if aentry not in self.bot.store['bot_admins']:
                self.bot.store['bot_admins'].append(aentry)
                await ctx.send(
                    'The user specified has successfully been added to the bot admin list!'
                )
            else:
                await ctx.send('The user specified is already a bot admin!')
        else:
            await ctx.send(
                ctx.mention +
                ' You are not a bot admin, so you may not add others as admins!'
            )

    @commands.command(aliases=['deladmin', 'admindel', 'adminrm'])
    async def rmadmin(self, ctx, *rrtarget: str):
        """Remove a user from the bot admin list.
        Usage: rmadmin [user]"""
        tmp = check_perms(ctx, ('bot_admin', ))
        if not rrtarget:
            await ctx.send(
                '**You need to specify a name, nickname, name#discriminator, or ID!**'
            )
            return
        rtarget = ' '.join(rrtarget)
        try:
            _target = ctx.guild.get_member_named(rtarget)
        except AttributeError:
            _target = None
        if _target:
            target = _target.id
        elif len(rtarget) in range(15, 21):
            target = rrtarget[0]
        else:
            await ctx.send(
                '**Invalid name! Name, nickname, name#discriminator, or ID work.**'
            )
            return
        if tmp:
            aentry = target
            try:
                self.bot.store['bot_admins'].remove(aentry)
            except ValueError:
                await ctx.send('The user specified is not a bot admin!')
            else:
                await ctx.send(
                    'The user specified has successfully been demoted!')
        else:
            await ctx.send(
                ctx.mention +
                ' You are not a bot admin, so you may not demote other admins!'
            )

    @commands.command(aliases=['admins'])
    async def adminlist(self, ctx):
        """List all bot admins defined.
        Usage: adminlist"""
        alist = ''
        for i in self.bot.store['bot_admins']:
            nid = ''
            try:
                _name = ctx.guild.get_member(i)
            except AttributeError:
                _name = None
            if not _name:
                try:
                    _name = await self.bot.get_user_info(i)
                except discord.NotFound:
                    _name = 'UNKNOWN'
                    nid = i
            if not nid:
                nid = _name.id
            alist += '**' + str(_name) + f'** (ID `{nid}`)\n'
        await ctx.send('The following people are bot admins:\n' + alist)

    @commands.command()
    @commands.check(commands.guild_only())
    async def getprop(self, ctx, pname: str):
        """Fetch a property from the datastore.
        Usage: getprop [property name]"""
        try:
            pout = self.bot.store.get_prop(ctx.message, pname)
        except Exception:
            await ctx.send('⚠ An error occured.')
            return
        await ctx.send(pout)

    @commands.command()
    @commands.check(commands.guild_only())
    async def setprop(self, ctx, pname: str, *, value: str):
        """Set the value of a property on guild level.
        Usage: setprop [property name] [value]"""
        echeck_perms(ctx, ('manage_guild', ))
        self.bot.store.set_prop(ctx.message, 'by_guild', pname, value)
        await ctx.send(':white_check_mark:')

    @commands.command(aliases=['getprefix', 'setprefix'])
    async def prefix(self, ctx, *prefix: str):
        """Get or set the command prefix.
        Usage: prefix {new prefix}"""
        sk = ' guild'
        prop = ('by_guild', 'command_prefix')
        if self.bot.selfbot:
            sk = ''
            prop = ('global', 'selfbot_prefix')
        if prefix:
            or_check_perms(
                ctx, ['manage_guild', 'manage_channels', 'manage_messages'])
            jprefix = ' '.join(prefix)
            self.bot.store.set_prop(ctx.message, *prop, jprefix)
            await ctx.send(':white_check_mark:')
        else:
            oprefix = self.bot.store.get_cmdfix(ctx.message)
            await ctx.send('**Current%s command prefix is: **`%s`' %
                           (sk, oprefix))

    async def progress(self, msg: discord.Message, begin_txt: str):
        """Play loading animation with dots and moon."""
        fmt = '{0}{1} {2}'
        anim = '🌑🌒🌓🌔🌕🌝🌖🌗🌘🌚'
        anim_len = len(anim) - 1
        anim_i = 0
        dot_i = 1
        while True:
            await msg.edit(content=fmt.format(begin_txt, ('.' * dot_i) + ' ' *
                                              (3 - dot_i), anim[anim_i]))
            dot_i += 1
            if dot_i > 3:
                dot_i = 1
            anim_i += 1
            if anim_i > anim_len:
                anim_i = 0
            await asyncio.sleep(1.1)

    @commands.command()
    @commands.check(commands.guild_only())
    async def mute(self, ctx, *, member: discord.Member):
        """Mute someone on voice and text chat.
        Usage: mute [person's name]"""
        or_check_perms(ctx, [
            'mute_members', 'manage_roles', 'manage_channels',
            'manage_messages'
        ])
        status = await ctx.send('Muting... 🌚')
        pg_task = self.loop.create_task(
            asyncio.wait_for(self.progress(status, 'Muting'),
                             timeout=30,
                             loop=self.loop))
        try:
            ch_perms = discord.PermissionOverwrite(
                **{p: False
                   for p in muted_perms})
            for channel in ctx.guild.channels:
                await channel.set_permissions(member, ch_perms)
            await member.__redit(mute=True,
                                 deafen=None,
                                 reason='Mute command was used on user')
            pg_task.cancel()
            await status.delete(reason='Deleting progress/status message')
            await ctx.send('Successfully muted **%s**!' % str(member))
        except (discord.Forbidden, discord.HTTPException):
            pg_task.cancel()
            await status.delete(reason='Deleting progress/status message')
            await ctx.send('**I don\'t have enough permissions to do that!**')

    @commands.command()
    @commands.check(commands.guild_only())
    async def unmute(self, ctx, *, member: discord.Member):
        """Unmute someone on voice and text chat.
        Usage: unmute [person's name]"""
        or_check_perms(ctx, ('mute_members', 'manage_roles', 'manage_channels',
                             'manage_messages'))
        status = await ctx.send('Unmuting... 🌚')
        pg_task = self.loop.create_task(
            asyncio.wait_for(self.progress(status, 'Unmuting'),
                             timeout=30,
                             loop=self.loop))
        role_map = {r.name: r for r in member.roles}
        try:
            if 'Muted' in role_map:
                await member.remove_roles(
                    role_map['Muted'],
                    reason='Unmute command was used on user')
            ch_perms = discord.PermissionOverwrite(
                **{p: None
                   for p in muted_perms})
            for channel in ctx.guild.channels:
                await channel.set_permissions(member, ch_perms)
            await member.__redit(mute=False,
                                 deafen=None,
                                 reason='Unmute command was used on user')
            pg_task.cancel()
            await status.delete(reason='Deleting progress/status message')
            await ctx.send('Successfully unmuted **%s**!' % str(member))
        except (discord.Forbidden, discord.HTTPException):
            pg_task.cancel()
            await status.delete(reason='Deleting progress/status message')
            await ctx.send('**I don\'t have enough permissions to do that!**')

    @commands.command()
    @commands.check(commands.guild_only())
    async def ban(self, ctx, *, member: discord.Member):
        """Ban someone from the guild.
        Usage: ban [member]"""
        echeck_perms(ctx, ('ban_members', ))
        await ctx.send(':hammer: **Are you sure you want to ban ' +
                       member.mention + '?**')
        if not (await self.bot.wait_for(
                'message',
                timeout=6.0,
                check=lambda m: m.content.lower().startswith('y') and m.channel
                == ctx.channel and m.author == ctx.author)):
            await ctx.send('Not banning.')
            return
        await member.ban(
            reason=
            'Ban was requested by command (from someone with the Ban Members permission)'
        )
        await ctx.send(':hammer: Banned. It was just about time.')
Example #25
0
class Channel(commands.Cog):
    """Channel related commands."""
    def __init__(self, bot):
        self.bot = bot

    global confirm
    confirm = "confirm"

    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_messages=True),
                        commands.guild_only())
    @commands.group(
        name="channel",
        aliases=["ch"],
        brief="Useful channel management commands",
        help=
        "Channel management commands, use the help command for a list of commands",
        invoke_without_command=True,
        case_insensitive=True)
    async def _channel(self, ctx):
        await ctx.send_help(ctx.command)

    @_channel.command(
        name="rename",
        aliases=["re"],
        brief="Renames channel.",
        help=
        "Renames the current channel or mentioned channel if argument passed.")
    async def _rename(self, ctx, channel, newname):
        pass

    #togglelock
    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_channels=True),
                        commands.guild_only())
    @_channel.command(
        name="togglelock",
        aliases=['tl'],
        brief="Locks/Unlocks a channel, and optionally renames channel",
        case_insensitive=True,
        help=
        "Toggles send_messages perms for everyone. And renames channel if an argument is passed.)"
    )
    async def _togglelock(self, ctx, *, channel_name=None):
        overwrite = ctx.channel.overwrites_for(ctx.guild.default_role)
        ch_name = channel_name

        try:
            if ch_name != None:
                channel = ctx.channel
                await channel.edit(name=ch_name)
                await ctx.send(f"Changed channel name to {ch_name}")

            if overwrite.send_messages != False:
                await ctx.channel.set_permissions(ctx.guild.default_role,
                                                  send_messages=False)
                await ctx.send("Locked.")

            if overwrite.send_messages == False:
                await ctx.channel.set_permissions(ctx.guild.default_role,
                                                  send_messages=True)
                await ctx.send("Unlocked.")
        except Exception as e:
            raise e

    #lock
    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_channels=True),
                        commands.guild_only())
    @_channel.command(name="lock",
                      brief="Locks channel(s).",
                      help="Lock current/all channel(s)")
    async def _lock(self, ctx, channel=None):
        overwrite = ctx.channel.overwrites_for(ctx.guild.default_role)

        if channel == None and overwrite.send_messages == True:
            await ctx.channel.set_permissions(ctx.guild.default_role,
                                              send_messages=False)
            await ctx.send("Locked.")

        if channel == None and overwrite.send_messages != True:
            await ctx.send("This channel is already locked.")

        if channel == "all":
            await ctx.send(
                f"This will **lock** *all* channels. Type `{confirm}` to confirm."
            )

            def check(m):
                return m.channel.id == ctx.channel.id and m.author.id == ctx.author.id

            try:
                msg = await self.bot.wait_for("message",
                                              timeout=30,
                                              check=check)
            except asyncio.TimeoutError:
                return await ctx.send("Time's up. Aborted.")
            if msg.content.lower() != "confirm":
                return await ctx.send("Aborted.")

            msg = await ctx.send("Locking all channels...")
            for c in ctx.guild.channels:
                if overwrite.send_messages != False:
                    await c.set_permissions(ctx.guild.default_role,
                                            send_messages=False)
            await ctx.send("Locked all channels ✅.")

    #unlock
    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_channels=True),
                        commands.guild_only())
    @_channel.command(name="unlock",
                      brief="Unlocks channel(s).",
                      help="Unlock current/all channel(s)")
    async def _unlock(self, ctx, channel=None):
        overwrite = ctx.channel.overwrites_for(ctx.guild.default_role)

        if channel == None and overwrite.send_messages == False:
            await ctx.channel.set_permissions(ctx.guild.default_role,
                                              send_messages=True)
            await ctx.send("Unlocked.")

        if channel == None and overwrite.send_messages != False:
            await ctx.send("This channel is already unlocked.")

        if channel == "all":
            await ctx.send(
                f"This will **unlock** *all* channels. Type `{confirm}` to confirm."
            )

            def check(m):
                return m.channel.id == ctx.channel.id and m.author.id == ctx.author.id

            try:
                msg = await self.bot.wait_for("message",
                                              timeout=30,
                                              check=check)
            except asyncio.TimeoutError:
                return await ctx.send("Time's up. Aborted.")
            if msg.content.lower() != "confirm":
                return await ctx.send("Aborted.")

            msg = await ctx.send("Unlocking all channels...")
            for c in ctx.guild.channels:
                if overwrite.send_messages != True:
                    await c.set_permissions(ctx.guild.default_role,
                                            send_messages=True)
            await ctx.send("Unlocked all channels ✅.")

    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_channels=True),
                        commands.guild_only())
    @_channel.group(
        name="strip",
        aliases=["split"],
        brief="Strips and renames channels.",
        help=
        f"Strips off the second part of every channels' name after the separator, and only keeps the first half before the separator\n\nsingle channel\n\n{prefix}channel strip <channel> <separator>.",
        invoke_without_command=True,
        case_insensitive=True)
    async def _strip(self,
                     ctx,
                     channel: discord.TextChannel = None,
                     *,
                     separator):
        channel = channel or ctx.channel
        stripped = channel.name.split(separator)[0]
        channel_name = channel.name
        if channel != "all" and channel != "current":
            await ctx.send(
                f"This will **rename** {channel.mention} to **{stripped}**. This action cannot be undone. Type `{confirm}` to confirm."
            )

        def check(m):
            return m.channel.id == ctx.channel.id and m.author.id == ctx.author.id

        try:
            msg = await self.bot.wait_for("message", timeout=30, check=check)
        except asyncio.TimeoutError:
            return await ctx.send("Time's up. Aborted.")
        if msg.content.lower() != "confirm":
            return await ctx.send("Aborted.")

        if channel != "all" and channel != "current":
            await ctx.send(
                f"Stripping {channel.mention}'s name with separator `{separator}` ..."
            )
            if separator in channel_name:
                new_name = stripped
                await channel.edit(name=new_name)
            await ctx.send(f"Done stripping the name of {channel.mention} ✅.")

    @_strip.command(name="current",
                    brief="Strips current channel.",
                    help="Strips the channel the command was used in.")
    async def _current(self, ctx, separator):

        channel = ctx.channel
        channel_name = channel.name
        stripped = channel.name.split(separator)[0]
        await ctx.send(
            f"This will **rename** {channel.mention} to **{stripped}**. This action cannot be undone. Type `{confirm}` to confirm."
        )

        def check(m):
            return m.channel.id == ctx.channel.id and m.author.id == ctx.author.id

        try:
            msg = await self.bot.wait_for("message", timeout=30, check=check)
        except asyncio.TimeoutError:
            return await ctx.send("Time's up. Aborted.")
        if msg.content.lower() != "confirm":
            return await ctx.send("Aborted.")

        await ctx.send(
            f"Stripping {channel.mention}'s name with separator `{separator}` ..."
        )
        if separator in channel_name:
            new_name = stripped
            await channel.edit(name=new_name)
        await ctx.send(f"Done stripping the name of {channel.mention} ✅.")

    @_strip.command(name="all",
                    brief="Strips all channels.",
                    help="Strips all channels in a server.")
    async def _all(self, ctx, separator):

        await ctx.send(
            f"This will **strip with `{separator}` and rename** *all* channels. This action cannot be undone. Type `{confirm}` to confirm."
        )

        def check(m):
            return m.channel.id == ctx.channel.id and m.author.id == ctx.author.id

        try:
            msg = await self.bot.wait_for("message", timeout=30, check=check)
        except asyncio.TimeoutError:
            return await ctx.send("Time's up. Aborted.")
        if msg.content.lower() != "confirm":
            return await ctx.send("Aborted.")

        await ctx.send(
            f"Stripping all channels' names with separator ` {separator} ` ..."
        )
        for channel in ctx.guild.channels:
            stripped = channel.name.split(separator)[0]
            channel_name = channel.name
            if separator in channel_name:
                new_name = stripped
                await channel.edit(name=new_name)
        await ctx.send("Done stripping all channels' names ✅.")
Example #26
0
 def __init__(self, bot):
     super().__init__(bot)
     for cmd in self.get_commands():
         cmd.add_check(commands.guild_only())
         if cmd.name == '환영메시지':
             cmd.add_check(self.check.subcmd_valid)
Example #27
0
class Utility(Cog):
    """Random commands that can be useful here and there.
    Settings, properties, and other stuff can be found here.
    """
    def __init__(self, bot):
        super().__init__(bot)
        self.logger = self.logger.getChild('utility')

    @commands.command()
    @commands.check(commands.guild_only())
    async def icon(self, ctx):
        """Retrive the current guild's icon.
        Usage: icon"""
        sname = '**' + ctx.guild.name + '**'
        iurl = ctx.guild.icon_url
        if iurl:
            await ctx.send('Here is the link to the icon for ' + sname +
                           ': <' + iurl + '>')
        else:
            await ctx.send('The current guild, ' + sname +
                           ', does not have an icon set! :slight_frown:')

    @commands.command(aliases=['echo'])
    async def say(self, ctx, *, message: str):
        """Repeat your message.
        Usage: say [message]"""
        await ctx.send(message)

    @commands.command(aliases=['whois', 'who', 'userinfo', 'uinfo', 'u'])
    async def user(self, ctx, *users: str):
        """Get tons of info on an user or some users.
        Spaces, multiuser, and cross-guild IDs work.
        Usage: user {user(s)}"""
        targets = []
        s = ctx.guild
        if users:  # huge complicated mess for spaces,
            # multiuser, nicknames, mentions, IDs,
            # names, and more in one go.
            members = {}
            for i in getattr(s, 'members', []):
                members[i.mention] = i
                members[i.id] = i
                members[i.display_name] = i
                members[i.name] = i
                members[str(i)] = i
            for i in users:
                try:
                    member = s.get_member(i)
                except AttributeError:
                    try:
                        member = await self.bot.get_user_info(i)
                    except discord.HTTPException:
                        member = None
                if member:
                    targets.append(member)
                else:
                    try:
                        member = await self.bot.get_user_info(i)
                    except discord.HTTPException:
                        member = None
                    if member:
                        targets.append(member)
            names = []
            _i = 0
            while _i < len(users):
                names.append(users[_i])
                with suppress(KeyError):
                    if ' '.join(names) in members:
                        targets.append(members[' '.join(names)])
                        names = []
                    elif _i + 1 == len(users):
                        targets.append(members[users[0]])
                        _i = -1
                        users = users[1:]
                        names = []
                _i += 1
            if not targets:
                await ctx.send(
                    '**No matching users, try again! Name, nickname, name#0000 (discriminator), or ID work. Spaces do, too!**'
                )
                return
        else:
            targets.append(ctx.author)
        targets = list(OrderedDict.fromkeys(targets))
        for target in targets:
            d_name = target.display_name
            avatar_url = target.avatar_url
            try:
                t_roles = target.roles
            except AttributeError:
                t_roles = []
            try:
                t_game = target.game
            except AttributeError:
                t_game = None
            is_guild = ctx.guild is not None
            with suppress(ValueError, AttributeError):
                t_roles.remove(target.guild.default_role)
            r_embed = discord.Embed(color=random.randint(0, 255**3 - 1))
            r_embed.set_author(name=str(target),
                               icon_url=avatar_url,
                               url=avatar_url)
            r_embed.set_thumbnail(url=avatar_url)  #top right
            r_embed.set_footer(text=str(target), icon_url=avatar_url)
            r_embed.add_field(
                name='Nickname',
                value=('None' if d_name == target.name else d_name))
            r_embed.add_field(name='User ID', value=target.id)
            r_embed.add_field(name='Creation Time',
                              value=target.created_at.strftime(absfmt))
            r_embed.add_field(name='Guild Join Time',
                              value=target.joined_at.strftime(absfmt)
                              if is_guild else 'Couldn\'t fetch')
            r_embed.add_field(
                name='Roles',
                value=', '.join([str(i)
                                 for i in t_roles]) if t_roles else 'None')
            r_embed.add_field(name='Status',
                              value=status_map[str(target.status)]
                              if is_guild else 'Couldn\'t fetch')
            try:
                r_embed.add_field(name='Currently Playing',
                                  value=(str(t_game) if t_game else 'Nothing'))
            except TypeError:
                r_embed.add_field(name='Currently Playing', value='Nothing 😦')
            await ctx.send(embed=r_embed)

    @commands.command(aliases=['sinfo', 'serverinfo', 'guild', 'ginfo'],
                      no_pm=True)
    async def guildinfo(self, ctx):
        """Get loads of info about this guild.
        Usage: guildinfo"""
        s = ctx.guild
        ach = s.channels
        chlist = [len(ach), 0, 0]
        for i in ach:
            if isinstance(i, discord.TextChannel):
                chlist[1] += 1
            else:
                chlist[2] += 1
        iurl = s.icon_url
        s_reg = str(s.region)
        r_embed = discord.Embed(color=random.randint(0, 255**3 - 1))
        if iurl:
            thing = {'url': iurl}
        else:
            thing = {}
        r_embed.set_author(name=s.name,
                           **thing,
                           icon_url=(iurl if iurl else ctx.me.avatar_url))
        r_embed.set_footer(text=ctx.me.display_name,
                           icon_url=ctx.me.avatar_url)
        if iurl:
            r_embed.set_image(url=iurl)
        r_embed.add_field(name='ID', value=s.id)
        r_embed.add_field(name='Members', value=len(s.members))
        r_embed.add_field(name='Channels',
                          value=ch_fmt.format(*[str(i) for i in chlist]))
        r_embed.add_field(name='Roles', value=len(s.roles))
        r_embed.add_field(name='Custom Emojis', value=len(s.emojis))
        r_embed.add_field(name='Region (Location)',
                          value=str(s.region).replace(
                              '-', ' ').title().replace('Eu ', 'EU ').replace(
                                  'Us ', 'US ').replace('Vip', 'VIP '))
        r_embed.add_field(name='Owner', value=str(s.owner))
        r_embed.add_field(
            name='Default Channel',
            value=f'<#{s.default_channel.id}>\n(#{s.default_channel.name})'
            if s.default_channel is not None else 'None (deleted)')
        r_embed.add_field(name='Admins Need 2FA',
                          value=('Yes' if s.mfa_level else 'No'))
        r_embed.add_field(name='Verification Level',
                          value=v_level_map[str(s.verification_level)])
        await ctx.send(embed=r_embed)

    @commands.command(aliases=['goldmine', 'about'])
    async def info(self, ctx):
        """Get bot info.
        Usage: info"""
        ach = self.bot.get_all_channels()
        chlist = [0, 0, 0]
        for i in ach:
            chlist[0] += 1
            if isinstance(i, discord.TextChannel):
                chlist[1] += 1
            else:
                chlist[2] += 1
        up = self.bot.format_uptime()
        ram = self.bot.get_ram()
        got_conversion = ram[0]
        emb = discord.Embed(color=random.randint(0, 255**3 - 1))
        emb.set_author(name=ctx.me.display_name,
                       url='https://khronodragon.com/',
                       icon_url=ctx.me.avatar_url)
        emb.set_footer(
            text='Made in Python 3.6+ with Discord.py %s' %
            self.bot.lib_version,
            icon_url=
            'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Python-logo-notext.svg/400px-Python-logo-notext.svg.png'
        )
        emb.add_field(name='Guilds', value=len(self.bot.guilds))
        emb.add_field(name='Author', value='Dragon5232#1841')
        emb.add_field(name='Uptime', value=up)
        emb.add_field(name='Local Time',
                      value=time.strftime(absfmt, time.localtime()))
        emb.add_field(name='Cogs Loaded', value=len(self.bot.cogs))
        emb.add_field(name='Command Calls',
                      value=sum(self.bot.command_calls.values()))
        emb.add_field(name='Memory Used',
                      value=(str(round(ram[1], 1)) +
                             ' MB (%s MiB)' % str(round(ram[2], 1)))
                      if got_conversion else 'Couldn\'t fetch')
        emb.add_field(name='Members Seen',
                      value=sum(g.member_count for g in self.bot.guilds))
        emb.add_field(name='Channels',
                      value=ch_fmt.format(*[str(i) for i in chlist]))
        emb.add_field(name='Custom Emojis', value=len(self.bot.emojis))
        emb.add_field(name='Commands', value=str(len(self.bot.all_commands)))
        emb.add_field(name='ID', value=ctx.me.id)
        if self.bot.user.id == 239775420470394897:
            emb.add_field(name='Invite Link', value='https://tiny.cc/goldbot')
        await ctx.send(home_broadcast, embed=emb)

    @commands.cooldown(1, 2.95, type=commands.BucketType.guild)
    @commands.command(aliases=['pong', 'latency'])
    async def ping(self, ctx):
        """Ping, pong!
        Usage: ping"""
        begin_time = datetime.now()
        msg = await ctx.send('**Pong!** | I wonder how long this takes...')
        await msg.edit(content='**Pong!** | I really do wonder...')
        time_diff = datetime.now() - begin_time
        await msg.edit(content='**Pong!** Responded in {}ms.'.format(
            round((time_diff.total_seconds() / 2) * 1000, 2)))

    @commands.command(aliases=['ram', 'memory', 'mem'])
    async def uptime(self, ctx):
        """Report the current uptime of the bot.
        Usage: uptime"""
        up = self.bot.format_uptime()
        ram = self.bot.get_ram()
        got_conversion = ram[0]
        ram_final = (' RAM usage is **' + str(round(ram[1], 1)) +
                     ' MB (%s MiB)**.' %
                     str(round(ram[2], 1))) if got_conversion else ''
        await ctx.send(ctx.mention + ' I\'ve been up for **' + up + '**.' +
                       ram_final)

    @commands.command(aliases=['addbot'])
    async def invite(self, ctx, *rids: str):
        """Generate an invite link for myself or another bot.
        Usage: invite {optional: bot ids}"""
        ids = list(rids)
        msg = []
        if not ids:
            ids.append(self.bot.user.id)
        for iid in ids:
            try:
                int(iid)
                if len(iid) in range(16, 20):
                    if iid == self.bot.user.id:
                        msg.append(
                            '<https://discordapp.com/api/oauth2/authorize?client_id={}&scope=bot&permissions={}>'
                            .format(iid, self.bot.perm_mask))
                    else:
                        msg.append(
                            '<https://discordapp.com/api/oauth2/authorize?client_id=%s&scope=bot&permissions=3072>'
                            % iid)
                else:
                    msg.append('**Invalid ID!**')
            except ValueError:
                msg.append('**Invalid ID!**')
        if msg:
            await ctx.send('\n'.join(msg))

    @commands.command(aliases=['website'])
    async def home(self, ctx):
        """Get my "contact" info.
        Usage: home"""
        await ctx.send(home_broadcast)

    async def poll_task(self, emojis, msg, poll_table):
        while True:
            user, r = await self.bot.wait_for(
                'reaction_add',
                check=lambda r, u: r.message == msg and u != msg.guild.me and r
                .emoji in emojis)
            if user not in poll_table[str(r.emoji)]:
                poll_table[str(r.emoji)].append(user)

    @commands.cooldown(1, 10, type=commands.BucketType.user)
    @commands.command()
    async def poll(self, ctx, *rquestion: str):
        """Start a public poll with reactions.
        Usage: poll [emojis] [question] [time in seconds]"""
        async def cem_help(emojis, raw_c_emojis, cem_map, c_emojis):
            """Custom emoji helper."""
            if raw_c_emojis:
                try:
                    for i in ctx.guild.emojis:
                        cem_map[str(i)] = i
                except AttributeError:
                    return
                for i in raw_c_emojis:
                    try:
                        c_emojis.append(cem_map[i])
                    except KeyError:
                        await ctx.send(
                            '**Custom emoji `%s` doesn\'t exist!**' % i)
                        return
                emojis += c_emojis

        question = ''
        if rquestion:
            question = ' '.join(rquestion)
        else:
            await ctx.send('**You must specify a question!**')
            return
        stime = 0.0
        cem_map = {}
        highpoints = None
        try:
            stime = float(rquestion[-1:][0])
        except ValueError:
            await ctx.send('**You must provide a valid poll time!**')
            return
        _question = question.split()
        del _question[-1:]
        question = ' '.join(_question)
        try:  # UCS-4
            highpoints = re.compile(u'[\U00010000-\U0010ffff\u2615]')
        except re.error:  # UCS-2
            highpoints = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
        u_emojis = re.findall(highpoints, question)
        raw_c_emojis = re.findall(
            re.compile(r'<:[a-z]+:[0-9]{18}>', flags=re.IGNORECASE), question)
        c_emojis = []
        emojis = u_emojis
        await cem_help(emojis, raw_c_emojis, cem_map, c_emojis)
        emojis = list(OrderedDict.fromkeys(emojis))
        for ri in emojis:
            i = str(ri)
            question = question.replace(' ' + i, '')
            question = question.replace(i + ' ', '')
            question = question.replace(i, '')
        question = question.strip()
        if not emojis:
            await ctx.send('**You must specify some emojis!**')
            return
        elif len(emojis) < 2:
            await ctx.send('**You need at least 2 emojis to poll!**')
            return
        msg_key = ctx.mention + ' is now polling:\n    \u2022 ' + question + '\n'
        msg = await ctx.send(msg_key + '**Adding reactions, please wait...**')
        for emoji in emojis:
            await msg.add_reaction(emoji)
            await asyncio.sleep(0.14)
        await msg.edit(content=msg_key + '**Poll is active, vote!**')
        emojis = list(emojis)
        poll_table = OrderedDict((str(i), []) for i in emojis)
        task = self.loop.create_task(self.poll_task(emojis, msg, poll_table))
        await asyncio.sleep(stime)
        task.cancel()
        _vote_table = {i: len(poll_table[i]) for i in poll_table}
        vote_table = OrderedDict(
            reversed(sorted(_vote_table.items(), key=lambda t: t[1])))
        _totals = '\n'.join([
            str(i) + ': {0} votes'.format(str(vote_table[i]))
            for i in vote_table
        ])
        winner = max(vote_table, key=vote_table.get)
        await ctx.send('**Poll time is over, stopped! Winner is...** ' +
                       str(winner) + '\nResults were:\n' + _totals)
        await msg.edit(content=msg_key + '**Poll ended.**')

    @commands.command(aliases=['memegen'], hidden=True)
    async def meme(self, ctx, *, pre_text: str):
        """Generate a meme!
        Usage: meme [top text] [bottom text]"""
        char_table = {
            '-': '--',
            '_': '__',
            '?': '~q',
            '%': '~p',
            '#': '~h',  # TODO: make
            '/': '~s',
            '"': "''",
            '\n': ' '
        }
        for key in char_table:
            pre_text = pre_text.replace(key, char_table[key])
        pre_text = pre_text.replace('    ', '__bottom__')
        pre_text = pre_text.replace(' ', '-')
        if '__bottom__' in pre_text:
            segments = pre_text.split('__bottom__')
        else:
            segments = textwrap.wrap(pre_text, width=int(len(pre_text) / 2))
        with async_timeout.timeout(10):
            async with self.bot.cog_http.get(
                    'https://memegen.link/api/templates/') as r:
                rtext = await r.text()
                templates = list(json.loads(rtext).values())
            rtemp = random.choice(templates)
            meme_url = rtemp + '/' + segments[0] + '/' + segments[1] + '.jpg'
            async with self.bot.cog_http.get(meme_url) as r:
                raw_image = await r.read()
        await ctx.send(file=discord.File(BytesIO(raw_image), 'meme.jpg'))

    @commands.command(aliases=['statistics', 'guilds', 'channels', 'users'])
    async def stats(self, ctx):
        """Show some basic stats.
        Usage: stats"""
        fmt = '''Stats: (get more with `{1}info`)
**Guilds**: {2}
**Channels**: {3}
**Members**: {4}
**Uptime**: {5}'''
        up = self.bot.format_uptime()
        await ctx.send(
            fmt.format(ctx.message, ctx.prefix, len(self.bot.guilds),
                       sum(len(s.channels) for s in self.bot.guilds),
                       sum(len(s.members) for s in self.bot.guilds), up))

    @commands.command(aliases=['randcolor', 'rc', 'randcolour', 'rcolour'])
    async def rcolor(self, ctx):
        """Generate a random color.
        Usage: rcolor"""
        col_rgb = [random.randint(1, 255) for i in range(0, 3)]
        col_str = '0x%02X%02X%02X' % (col_rgb[0], col_rgb[1], col_rgb[2])
        await ctx.send(
            embed=discord.Embed(color=int(col_str, 16),
                                title='Hex: ' + col_str.replace('0x', '#') +
                                ' | RGB: ' +
                                ', '.join([str(c) for c in col_rgb]) +
                                ' | Integer: ' + str(int(col_str, 16))))

    @commands.command(aliases=['character', 'char', 'cinfo', 'unicode'])
    async def charinfo(self, ctx, *, uchars: str):
        """Get the Unicode info for a character or characters.
        Usage: charinfo [character(s)]"""
        no_preview = ['\u0020', '\uFEFF']
        cinfo = commands.Paginator(
            prefix='',
            suffix='',
            max_size=(1999 if self.bot.selfbot else 2000))
        for char in list(uchars.replace('\n', '')):
            hexp = str(hex(ord(char))).replace('0x', '').upper()
            while len(hexp) < 4:
                hexp = '0' + hexp
            preview = f' (`{char}`)'
            cinfo.add_line(f'U+{hexp} {unicodedata.name(char)} {char}' +
                           (preview if char not in no_preview else ''))
        if len(cinfo.pages) > 5:
            await ctx.send('Too long, trimming to 5 pages.')
        for page in cinfo.pages[0:5]:
            await ctx.send(page)

    @commands.command()
    async def encode(self, ctx, *, content: str):
        """Encode your text into Goldcode!
        Usage: encode [text]"""
        await ctx.send('```' + (b_encode(content)) + '```')

    @commands.command()
    async def decode(self, ctx, *, content: str):
        """Decode your text from Goldcode!
        Usage: decode [encoded text]"""
        await ctx.send('```' + (b_decode(content)) + '```')

    @commands.cooldown(1, 6.75, type=commands.BucketType.user)
    @commands.command(aliases=['mc'])
    async def minecraft(self, ctx, *, server_ip: str):
        """Get information about a Minecraft server.
        Usage: minecraft [server address]"""
        port = 25565
        port_split = server_ip.split(':')
        server = port_split[0].replace('/', '')

        if len(port_split) > 1:
            try:
                port = int(port_split[1])
            except ValueError:
                pass
        if ('.' not in server) or (' ' in server_ip):
            await ctx.send(':warning: Invalid address.')
            return

        try:
            self.logger.info('Connecting to Minecraft server ' + server + ':' +
                             str(port) + '...')
            with async_timeout.timeout(5):
                data = await self.loop.run_in_executor(None, mclib.get_info,
                                                       server, port)
        except Exception as e:
            await ctx.send(
                f':warning: Couldn\'t get server info for `{server}:{port}`.')
            return

        desc = ''
        server_type = 'Vanilla'

        def decode_extra_desc():
            final = []
            format_keys = {
                'bold': '**',
                'italic': '*',
                'underlined': '__',
                'strikethrough': '~~'
            }
            for e in data['description']['extra']:
                item = e['text']
                for fkey in format_keys:
                    if e.get(fkey, False):
                        int_key = '%{f:' + fkey + '}$'
                        item = int_key + item + int_key
                final.append(item)
            final = ''.join(final)
            for fkey in format_keys:
                int_key = '%{f:' + fkey + '}$'
                final = final.replace(int_key * 3, '').replace(int_key * 2, '')
                final = final.replace(int_key, format_keys[fkey])
            return final

        if isinstance(data['description'], dict):
            if 'text' in data['description']:
                if data['description']['text']:
                    desc = data['description']['text']
                else:
                    desc = decode_extra_desc()
            else:
                desc = decode_extra_desc()
        elif isinstance(data['description'], str):
            desc = data['description']
        else:
            desc = str(data['description'])

        def decode_section_code():
            formats = {
                'l': '**',
                'n': '__',
                'o': '*',
                'k': '',
                'm': '~~',
                'k': '**',
                'r': ''
            }  # k = obf, r = reset
            state = ''

        desc = re.sub(r'\u00a7[4c6e2ab319d5f780lnokmr]', '', desc)
        emb = discord.Embed(title=server + ':' + str(port),
                            description=desc,
                            color=random.randint(0, 255**3 - 1))
        emb.set_footer(text=ctx.me.display_name, icon_url=ctx.me.avatar_url)
        emb.add_field(name='Players',
                      value=str(data['players']['online']) + '/' +
                      str(data['players']['max']))

        if data['players'].get('sample', False):
            content = re.sub(
                r'\u00a7[4c6e2ab319d5f78lnokmr]', '',
                smartjoin([p['name'] for p in data['players']['sample']]))
            if len(content) <= 1024:
                emb.add_field(name='Players Online', value=content)
            else:
                pages = textwrap.wrap(content, width=1024)
                for page in pages:
                    emb.add_field(name='Players Online', value=page)

        emb.add_field(name='Version',
                      value=re.sub(r'\u00a7[4c6e2ab319d5f78lnokmr]', '',
                                   data['version']['name']))
        emb.add_field(name='Protocol Version',
                      value=data['version']['protocol'])

        if 'modinfo' in data:
            if 'modList' in data['modinfo']:
                if data['modinfo']['modList']:
                    content = smartjoin([
                        m['modid'].title() + ' ' + m['version']
                        for m in data['modinfo']['modList']
                    ])
                    if len(content) <= 1024:
                        emb.add_field(name='Mods', value=content)
                    else:
                        pages = textwrap.wrap(content, width=1024)
                        for page in pages:
                            emb.add_field(name='Mods', value=page)
            if data['modinfo'].get('type', False):
                t = data['modinfo']['type']
                if t.lower() == 'fml':
                    server_type = 'Forge / FML'
                else:
                    server_type = t.title()

        emb.add_field(name='Server Type', value=server_type)
        emb.add_field(name='Ping',
                      value=str(round(data['latency_ms'], 2)) + 'ms')
        await ctx.send(embed=emb)

    @commands.cooldown(1, 20, type=commands.BucketType.user)
    @commands.command()
    async def contact(self, ctx, *, message: str):
        """Contact the bot owner with a message.
        Usage: contact [message]"""
        for m in ctx.message.mentions:
            message = message.replace(m.mention, '@' + str(m))
        msg_object = {
            'message': message,
            'user': str(ctx.author),
            'nick': ctx.author.display_name,
            'message_id': ctx.message.id,
            'user_id': ctx.author.id,
            'channel_id': ctx.channel.id,
            'pm': isinstance(msg.channel, discord.abc.PrivateChannel),
            'time': str(ctx.message.timestamp),
            'timestamp': ctx.message.timestamp.timestamp(),
            'contains_mention': bool(ctx.message.mentions),
        }
        if ctx.guild:
            msg_object.update({
                'guild': ctx.guild.name,
                'guild_id': ctx.guild.id,
                'guild_members': len(ctx.guild.members),
                'channel': ctx.channel.name
            })
        self.bot.store['owner_messages'].append(msg_object)
        await ctx.send(':thumbsup: Message recorded.')

    @commands.command(aliases=['rprof', 'rp'])
    async def rprofile(self, ctx):
        """Generate a random profile.
        Usage: rprofile"""
        name_overrides = {'cvv': 'CVV', 'cid': 'CID'}
        excluded = [
            'password', 'sexual_orientation', 'avatar', 'identifier', 'title',
            'language', 'paypal', 'worldview', 'views_on', 'political_views',
            'surname'
        ]
        if not ctx.author.avatar_url:
            ctx.author.avatar_url = ctx.author.default_avatar_url
        emb = discord.Embed(color=random.randint(1, 255**3 - 1))
        emb.set_author(name=ctx.author.display_name,
                       icon_url=ctx.author.avatar_url)
        p = elizabeth.Personal()
        traits = {}
        calls = {
            d: getattr(p, d)
            for d in dir(p)
            if (not d.startswith('_')) and hasattr(getattr(p, d), '__call__')
        }
        for call in calls:
            if call not in excluded:
                if call in name_overrides:
                    f_name = name_overrides[call]
                else:
                    f_name = call.replace('_', ' ').title()
                traits[f_name] = calls[call]()
        emb.set_thumbnail(url=p.avatar())
        emb.title = traits['Full Name'].split()[0]
        for trait in traits:
            emb.add_field(name=trait, value=str(traits[trait]))
        await ctx.send(embed=emb)

    @commands.cooldown(1, 5.75, type=commands.BucketType.user)
    @commands.command(aliases=['qr'])
    async def qrcode(self, ctx, *, text: str):
        """Create a QR code.
        Usage: qrcode [text to use]"""
        img_bytes = BytesIO()
        image = await self.loop.run_in_executor(None, qrcode.make, text)
        image.save(img_bytes, format='PNG')
        img_bytes.seek(0)
        await ctx.send(file=discord.File(img_bytes, 'qrcode.png'))

    @commands.command()
    async def avatar(self, ctx, *, target: discord.User):
        """Get someone's avatar.
        Usage: avatar [member]"""
        await ctx.send(target.avatar_url)

    @commands.command()
    async def ocr(self, ctx):
        """OCR an image.
        Usage: ocr [attach an image]"""
        or_check_perms(ctx, ('bot_owner', ))
        warnings.simplefilter('error', Image.DecompressionBombWarning)
        if ctx.message.attachments:
            with async_timeout.timeout(5):
                async with self.bot.cog_http.get(
                        ctx.message.attachments[0].proxy_url) as r:
                    raw_image = await r.read()
        else:
            await ctx.send(':warning: No attachment found.')
            return
        img_bytes = BytesIO(raw_image)
        image = Image.open(img_bytes)
        text = tesserocr.image_to_text(image)
        if text:
            await ctx.send(text)
        else:
            await ctx.send('No results.')

    @commands.command(aliases=['cm_discrim'])
    async def discrim(self, ctx, *, discriminator: str):
        """Look up users by discriminator.
        Usage: discrim [discriminator]"""
        d = discriminator
        targets = list(
            set(
                str(m) for m in self.bot.get_all_members()
                if m.discriminator == d))
        if targets:
            await ctx.send('**I found: **\n' + '\n'.join(targets))
        else:
            await ctx.send(
                'I found no matches. Maybe I\'m not in a guild with them?')

    @commands.command(aliases=['perms'])
    async def permissions(self, ctx):
        """Get your permissions here.
        Usage: permissions"""
        perms = [
            '**' + k[0].replace('_', ' ').title() + '**'
            for k in list(ctx.author.permissions_in(ctx.channel)) if k[1]
        ]
        if '**Administrator**' in perms:
            perms.remove('**Administrator**')
            perms.append('be an **administrator**')
        if '**Send Tts Messages**' in perms:
            perms[perms.index(
                '**Send Tts Messages**')] = '**Send TTS Messages**'
        await ctx.send('You can ' + smartjoin(perms) + '!')

    @commands.group(name='xkcd')
    async def cmd_xkcd(self, ctx):
        """Get a xkcd comic.
        Usage: xkcd {stuff}"""
        if ctx.invoked_subcommand is None:
            await self.bot.send_cmd_help(ctx)

    @commands.cooldown(1, 4, type=commands.BucketType.user)
    @cmd_xkcd.command(name='random')
    async def xkcd_random(self, ctx):
        """Get a random comic from xkcd.
        Usage: xkcd random"""
        comic = await xkcd.random_comic()
        emb = discord.Embed(color=random.randint(1, 255**3 - 1),
                            title=comic.title)
        emb.set_image(url=comic.image_link)
        emb.set_footer(text=comic.alt_text)
        await ctx.send(embed=emb)

    @commands.cooldown(1, 4, type=commands.BucketType.user)
    @cmd_xkcd.command(name='latest')
    async def xkcd_latest(self, ctx):
        """Get the latest comic from xkcd.
        Usage: xkcd latest"""
        comic = await xkcd.latest_comic()
        emb = discord.Embed(color=random.randint(1, 255**3 - 1),
                            title=comic.title)
        emb.set_image(url=comic.image_link)
        emb.set_footer(text=comic.alt_text)
        await ctx.send(embed=emb)

    @commands.cooldown(1, 4, type=commands.BucketType.user)
    @cmd_xkcd.command(name='number')
    async def xkcd_number(self, ctx, number: int):
        """Get the Nth comic from xkcd.
        Usage: xkcd number [number]"""
        try:
            comic = await xkcd.get_comic(number)
        except xkcd.InvalidComic:
            await ctx.send(':warning: That comic doesn\'t exist.')
            return
        emb = discord.Embed(color=random.randint(1, 255**3 - 1),
                            title=comic.title)
        emb.set_image(url=comic.image_link)
        emb.set_footer(text=comic.alt_text)
        await ctx.send(embed=emb)

    @commands.command(aliases=['zws', 'u200b', '200b'])
    async def zwsp(self, ctx, number: int = 1):
        """Output a number of ZWSPs.
        Usage: zwsp {number = 1}"""
        if number > 2000:
            await ctx.send('I can\'t give you more than 2000 ZWSPs.')
        elif number > 0:
            await ctx.send('\u200b' * number)
        else:
            await ctx.send('I can\'t give you zero ZWSPs.')

    @commands.command()
    async def b64decode(self, ctx, *, b64: str):
        """Decode some base64 data.
        Usage: b64decode [base64]"""
        br = base64.b64decode(b64)
        try:
            m = br.decode('utf-8')
        except ValueError:
            m = '```' + str(br)[2:][:-1] + '```'
        await ctx.send(m)

    @commands.command()
    async def b64encode(self, ctx, *, b64: str):
        """Encode some base64 data.
        Usage: b64encode [base64]"""
        br = base64.b64encode(b64.encode('utf-8'))
        try:
            m = br.decode('utf-8')
        except ValueError:
            m = '```' + str(br)[2:][:-1] + '```'
        await ctx.send(m)

    @commands.command(aliases=['ttsspam', 'tts_spam'])
    async def ttspam(self, ctx, *, text: str):
        """Spam a message with TTS. **This may get you banned from guilds.**
        Usage: ttspam [message]"""
        or_check_perms(ctx, ('manage_messages', ))
        m = await ctx.send(textwrap.wrap((text + ' ') * 2000, width=2000)[0],
                           tts=True)
        await asyncio.sleep(0.1)
        await m.delete(
            reason=
            'Deleting message used for a ttspam command (only usable by people with Manage Messages)'
        )

    @commands.command(aliases=['ip', 'rdns', 'reverse_dns', 'reversedns'])
    async def ipinfo(self, ctx, *, ip: str):
        """Get the GeoIP and rDNS data for an IP.
        Usage: ipinfo [ip/domain]"""
        emb = discord.Embed(color=random.randint(1, 255**3 - 1))
        emb.set_author(icon_url=ctx.me.avatar_url, name='IP Data')
        with async_timeout.timeout(5):
            async with self.bot.cog_http.get('https://freegeoip.net/json/' +
                                             ip) as r:
                data_res = await r.json()
        rdns = 'Failed to fetch'
        try:
            with async_timeout.timeout(6):
                rdns = (await
                        self.loop.run_in_executor(None, socket.gethostbyaddr,
                                                  data_res['ip']))[0]
        except Exception:
            pass
        emb.add_field(name='IP', value=data_res['ip'])
        emb.add_field(name='Reverse DNS', value=rdns)
        emb.add_field(name='Country',
                      value=data_res['country_name'] +
                      ' (%s)' % data_res['country_code'])
        region_val = data_res['region_name'] + ' (%s)' % data_res['region_code']
        emb.add_field(
            name='Region',
            value=(region_val if region_val != ' ()' else 'Not specified'))
        emb.add_field(
            name='City',
            value=(data_res['city'] if data_res['city'] else 'Not specified'))
        emb.add_field(name='ZIP Code',
                      value=(data_res['zip_code']
                             if data_res['zip_code'] else 'Not specified'))
        emb.add_field(name='Timezone',
                      value=(data_res['time_zone']
                             if data_res['time_zone'] else 'Not specified'))
        emb.add_field(name='Longitude', value=data_res['longitude'])
        emb.add_field(name='Latitude', value=data_res['latitude'])
        emb.add_field(name='Metro Code',
                      value=(data_res['metro_code'] if
                             data_res['metro_code'] != 0 else 'Not specified'))
        await ctx.send(embed=emb)

    @commands.command()
    async def dial(self, ctx, *, number: str):
        """Dial someone on the phone!
        Usage: dial [phone number]"""
        self.logger.info('Dialing ' + number + '...')
        await ctx.send(':telephone: **Dialing {}...**'.format(number))
        await asyncio.sleep((random.randint(1, 3) + random.uniform(0, 1)) *
                            random.uniform(0.4, 1.6) + random.uniform(0, 1))
        await ctx.send('**No answer.**')

    @commands.command(aliases=['define'])
    async def urban(self, ctx, *, term: str):
        """Define something, according to Urban Dictionary.
        Usage: urban [term]"""
        with async_timeout.timeout(5):
            async with self.bot.cog_http.get(
                    'http://api.urbandictionary.com/v0/define',
                    params={'term': term}) as r:
                data_res = await r.json()
        try:
            word = data_res['list'][0]
        except IndexError:
            await ctx.send('No results.')
            return

        emb = discord.Embed(color=random.randint(0, 255**3 - 1),
                            title=word['word'])
        emb.set_author(
            name='Urban Dictionary',
            url=word['permalink'],
            icon_url=
            'https://images.discordapp.net/.eJwFwdsNwyAMAMBdGICHhUPIMpULiCAlGIHzUVXdvXdf9cxLHeoUGeswJreVeGa9hCfVoitzvQqNtnTi25AIpfMuXZaBDSM4G9wWAdA5vxuIAQNCQB9369F7a575pv7KLUnjTvOjR6_q9wdVRCZ_.BorCGmKDHUzN6L0CodSwX7Yv3kg'
        )
        emb.set_footer(text=datetime.now().strftime(absfmt))

        definition = word['definition']
        if definition:
            def_pages = textwrap.wrap(definition, width=1024)
            for pg in def_pages[:3]:
                emb.add_field(name='Definition', value=pg, inline=False)
        else:
            emb.add_field(name='Definition', value='None?!?!', inline=False)

        example = word['example']
        if example:
            ex_pages = textwrap.wrap(example, width=1024)
            for pg in ex_pages[:3]:
                emb.add_field(name='Example', value=pg, inline=False)
        else:
            emb.add_field(name='Example', value='None?!?!', inline=False)

        emb.add_field(name='👍', value=word['thumbs_up'])
        emb.add_field(name='👎', value=word['thumbs_down'])
        await ctx.send(embed=emb)

    @commands.command(aliases=['nickname', 'setnick'])
    @commands.check(commands.guild_only())
    async def nick(self, ctx, *, nick: str):
        """Set your nickname.
        Usage: nick [new nickname]"""
        if ctx.author.guild_permissions.change_nickname:
            await ctx.author.edit(nick=nick,
                                  reason='User requested using command')
            await ctx.send(':thumbsup: Done.')
        else:
            await ctx.send(
                ':x: You don\'t have permission to change your nickname.')

    @commands.command()
    async def bleach(self, ctx):
        """Get some bleach. NOW.
        Usage: bleach"""
        emb = discord.Embed(color=random.randint(0, 255**3 - 1),
                            title='Bleach')
        emb.set_image(
            url=
            'https://upload.wikimedia.org/wikipedia/commons/d/d3/Clorox_Bleach_products.jpg'
        )
        await ctx.send(embed=emb)

    @commands.command()
    async def mcskin(self, ctx, *, username: str):
        """Get a Minecraft player's skin.
        Usage: mcskin [username]"""
        un = username.replace('\u200b', '').replace('/',
                                                    '').replace('\u200e', '')
        if not re.match(r'^[a-zA-Z0-9_]+$', un):
            await ctx.send(':warning: Invalid username.')
            return
        emb = discord.Embed(color=random.randint(0, 255**3 - 1),
                            title=un + "'s skin")
        emb.set_image(url='https://mcapi.ca/skin/' + un + '/150/true')
        await ctx.send(embed=emb)

    @commands.command()
    async def mchead(self, ctx, *, username: str):
        """Get a Minecraft player's head.
        Usage: mchead [username]"""
        un = username.replace('\u200b', '').replace('/',
                                                    '').replace('\u200e', '')
        if not re.match(r'^[a-zA-Z0-9_]+$', un):
            await ctx.send(':warning: Invalid username.')
            return
        emb = discord.Embed(color=random.randint(0, 255**3 - 1),
                            title=un + "'s head")
        emb.set_image(url='https://mcapi.ca/avatar/' + un + '/150/true')
        await ctx.send(embed=emb)
Example #28
0
class Useful(commands.Cog):
    """Useful commands"""
    def __init__(self, bot):
        self.bot = bot

    #wiki
    @commands.command(
        name="wiki",
        aliases=["wikipedia"],
        brief="Searches wikipedia for info.",
        help=
        "Use this command to look up anything on wikipedia. Sends the first 10 sentences from wikipedia."
    )
    async def wiki(self, ctx, *, arg=None):
        try:
            if arg == None:
                await ctx.send("Please, specify what do you want me to search."
                               )
            elif arg:
                msg = await ctx.send("Fetching...")
                start = arg.replace(" ", "")
                end = wikipedia.summary(start)
                await msg.edit(content=f"```py\n{end}\n```")
        except:
            try:
                start = arg.replace(" ", "")
                end = wikipedia.summary(start, sentences=10)
                await msg.edit(content=f"```py\n{end}\n```")
            except:
                await msg.edit(content="Not found.")

    # spam
    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_messages=True),
                        commands.guild_only())
    @commands.group(name="spam",
                    aliases=['sp'],
                    brief="Spams desired message",
                    invoke_without_command=True,
                    case_insensitive=True,
                    help="Spams desired message, with desired intervals.")
    async def spam(self, ctx):
        await ctx.send_help(ctx.command)

    # spam start
    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_messages=True))
    @spam.command(
        name="start",
        aliases=["s"],
        brief="Starts spam.",
        help=
        "Spams <message> every <delay> seconds. For multi-word messages put them within quotes"
    )
    @commands.max_concurrency(1, per=commands.BucketType.guild, wait=False)
    async def _spam_start(self, ctx, delay: float, *, msg):
        global on
        global sp_start_channel
        sp_start_channel = ctx.channel
        on = True
        while on:
            await ctx.send(msg)
            await asyncio.sleep(delay)

    # spam start error
    @_spam_start.error
    async def spamstart_error(self, ctx, error):
        if isinstance(error, commands.BadArgument):
            await ctx.send("The delay must be a number.")

    # spam stop
    @commands.check_any(commands.is_owner(),
                        commands.has_permissions(manage_messages=True))
    @spam.command(name="stop",
                  aliases=['end'],
                  brief="Stops running spam",
                  help="Stops a running `spam` command.")
    async def _spam_stop(self, ctx):
        global on
        global sp_start_channel
        sp_stop_channel = ctx.channel
        if on == True and sp_stop_channel == sp_start_channel:
            on = False
            await ctx.send("Stopped spam.")
        else:
            await ctx.send(
                "There isn't a `spam` command running in this channel.")

    # timer
    max_time = 600
    time = ""
    mins = ""
    tr_start_user = ""
    msg = ""
    secondint = ""

    @commands.group(
        name="timer",
        aliases=["tr", "countdown", "cd"],
        brief="Sets a timer",
        help=
        "Sets a timer, which the bot will count down from and ping at the end.",
        invoke_without_command=True,
        case_insensitive=True)
    async def timer(self, ctx):
        await ctx.send_help(ctx.command)

    #timer start
    @timer.command(
        name="start",
        aliases=["s"],
        brief="Starts timer.",
        help=
        f"Sets a timer for <seconds> and counts down from it(max {round(max_time/60, 2)}mins or {max_time}seconds). One timer per user at a time. Stop a running timer by using the {prefix}timer stop command."
    )
    @commands.max_concurrency(1, per=commands.BucketType.user, wait=False)
    async def _timer_start(self, ctx, seconds: int):

        global time
        time = round(seconds, 2)
        global mins
        mins = round(seconds / 60, 2)
        global tr_start_user
        tr_start_user = ctx.author
        global msg
        msg = f"{ctx.author.mention} time's up! ({mins}mins or {seconds}seconds)"
        global secondint
        secondint = seconds
        if seconds > max_time:
            await ctx.send(
                f"Timer can be set for max **{max_time/60}** minutes or **{max_time}** seconds"
            )
        elif seconds <= 0:
            await ctx.send("Please input a positive whole number.")
        else:
            message = await ctx.send(f"Timer: {secondint} seconds")
            while True:
                secondint -= 1
                if secondint == 0:
                    await message.edit(content=msg)
                    break
                await message.edit(content=f"Timer: {secondint} seconds.")
                await asyncio.sleep(1)

    @_timer_start.error
    async def timerstart_error(self, ctx, error):
        if isinstance(error, commands.BadArgument):
            await ctx.send("The time must be a positive whole number.")

    # timer stop
    @timer.command(name="stop",
                   aliases=["end"],
                   brief="Stops running timer",
                   help="Stops a running `timer` command.")
    async def _timer_stop(self, ctx):
        try:
            global msg
            global mins
            global time
            global tr_start_user
            global secondint
            if secondint > 0 and ctx.author == tr_start_user:
                msg = f"{ctx.author.mention} timer stopped! Stopped at {round(secondint/60, 2)}mins/{secondint}seconds **out of** {mins}mins/{time}seconds"
                secondint = 1
                await ctx.message.add_reaction('👍')
            else:
                await ctx.send(
                    f"There isn't a `timer` running that belongs to you.")
        except Exception as e:
            raise e
Example #29
0
class Cosmetic(Cog):
    """Commands for some neat-o fun!
    Includes color changing and more.
    """
    def __init__(self, bot):
        self.playing_anim = set()
        self.stop_anim = set()
        self.al_aliases = charsets.keys()
        super().__init__(bot)

    @commands.command()
    @commands.check(commands.guild_only())
    async def emotes(self, ctx):
        """Lists all the custom emoji im this guild.
        Usage: emotes"""
        cemotes = ctx.guild.emojis
        em_string = (' '.join([str(i) for i in cemotes]) if len(cemotes) >= 1
                     else 'This guild has no custom emojis!')
        await ctx.send(em_string)

    @commands.command(aliases=['rev', 'mirror'])
    async def reverse(self, ctx, *, rmsg: str):
        """Reverse some text you give.
        Usage: reverse [text here]"""
        await ctx.send(':repeat: ' + rmsg[::-1])

    @commands.command(aliases=[
        'math_sans_italic', 'circled', 'math_double', 'math_bold_italic',
        'math_sans_bold_italic', 'parenthesized', 'math_bold_fraktur',
        'math_sans_bold', 'squared', 'math_mono', 'fullwidth',
        'squared_negative', 'normal', 'circled_negative', 'regional',
        'math_sans', 'math_bold_script', 'math_bold', 'upside_down'
    ])
    async def style(self, ctx, *rmsg):
        """Stylize text in cool alphabets! Invoke with alphabet name.
        Usage: style [style name] [text here]"""
        if rmsg:
            imsg = ' '.join(rmsg)
            final_result = self.stylize(ctx.invoked_with.lower(), imsg)
            await ctx.send(final_result)
        else:
            await ctx.send(
                '**You must invoke this command as: `[p][name of set] [message here]`.** For example: `!math_bold hello world`! Here are the character sets available:'
            )
            await self.fontlist.invoke(ctx)

    def stylize(self, alphabet, intxt):
        return intxt.translate(
            str.maketrans(charsets['normal'], charsets[alphabet]))

    @commands.command(aliases=[
        'fonts', 'alphabet', 'alphabets', 'alphalist', 'styles', 'stylelist',
        'chars', 'charlist', 'charsets', 'charsetlist'
    ])
    async def fontlist(self, ctx):
        """List the available fancy character sets / alphabets / fonts.
        Usage: fonts"""
        pager = commands.Paginator(prefix='', suffix='')
        pager.add_line('**Listing all character sets defined with samples.**')
        for i in self.al_aliases:
            tmp = self.stylize(i, 'abcdefghijklmnopqrstuvwxyz')
            pager.add_line('**{0}**: `{1}`'.format(i, tmp))
        pager.add_line(
            '**Invoke with `[p][name of set] [message here]`.** For example: `!math_bold hello world`.'
        )
        for page in pager.pages:
            await ctx.send(page)

    @commands.cooldown(1, 6, type=commands.BucketType.guild)
    @commands.command(
        aliases=['af', 'sca', 'anim', 'a', 'playanim', 'aplay', 'animplay'])
    async def animation(self, ctx, anim_seq, runs: int):
        """Do a 0.9 fps animation x times from the given sequence.
        Usage: animation [packed animation] [number of runs]"""
        try:
            cmid = ctx.guild.id
        except AttributeError:
            cmid = ctx.author.id
        if cmid not in self.playing_anim:
            self.playing_anim.add(cmid)
            msg = await ctx.send('Starting animation...')
            for _xi in range(runs):
                for frame in anim_seq:
                    if cmid not in self.stop_anim:
                        await msg.edit(content=frame)
                        await asyncio.sleep(0.93)
                    else:
                        await msg.edit(content='**Animation stopped!**')
                        await ctx.send('**Animation stopped!**')
                        self.playing_anim.remove(cmid)
                        return
            await msg.edit(content='**Animation stopped!**')
            await ctx.send('**Animation stopped!**')
            self.playing_anim.remove(cmid)
        else:
            await ctx.send('**Already playing an animation in this guild!**')

    @commands.command(
        aliases=['sa', 'ssca', 'sanim', 'stopanimation', 'animstop', 'saf'])
    async def stopanim(self, ctx):
        """Stop the animation playing in this guild, if any.
        Usage: stopanim"""
        try:
            cmid = ctx.guild.id
        except AttributeError:
            cmid = ctx.author.id
        if cmid in self.playing_anim:
            await ctx.send('**Stopping animation...**')
            self.stop_anim.add(cmid)
            await asyncio.sleep(1.9)
            try:
                self.stop_anim.remove(cmid)
            except KeyError:
                pass
        else:
            await ctx.send('**Not playing any animation here!**')

    @commands.command(aliases=[
        'lanim', 'listanims', 'listanim', 'animationlist', 'animl', 'anims',
        'animations', 'al', 'packs', 'packed', 'pal', 'pa', 'alist'
    ])
    async def animlist(self, ctx):
        """List the packed animations I have saved.
        Usage: animlist"""
        await ctx.send('**Listing stored packed animations.**\n```\n' +
                       '\n'.join(spinners) + '```')

    @commands.command(aliases=[
        'random.cat', 'randomcat', 'rcat', 'cats', 'catrandom', 'random_cat'
    ])
    async def cat(self, ctx):
        """Get a random cat! Because why not.
        Usage: cat"""
        with async_timeout.timeout(8):
            async with self.bot.cog_http.get(
                    'http://random.cat/meow') as response:
                ret = await response.json()
        e = discord.Embed(color=random.randint(1, 255**3 - 1))
        e.set_image(url=ret['file'])
        await ctx.send(embed=e)

    @commands.command(
        aliases=['temote', 'bemote', 'dcemote', 'getemote', 'fetchemote'])
    async def emote(self, ctx, emote: str):
        """Get a Twitch, FrankerFaceZ, BetterTTV, or Discord emote.
        Usage: emote [name of emote]"""
        emote = emote.replace(':', '')
        with async_timeout.timeout(13):
            try:
                async with self.bot.cog_http.get(
                        'https://static-cdn.jtvnw.net/emoticons/v1/' +
                        str(self.bot.emotes['twitch'][emote]['image_id']) +
                        '/1.0') as resp:
                    emote_img = await resp.read()
            except KeyError:  # let's try frankerfacez
                try:
                    async with self.bot.cog_http.get(
                            'https://cdn.frankerfacez.com/emoticon/' +
                            str(self.bot.emotes['ffz'][emote]) + '/1') as resp:
                        emote_img = await resp.read()
                except KeyError:  # let's try BetterTTV
                    try:
                        async with self.bot.cog_http.get(
                                self.bot.emotes['bttv'][emote]) as resp:
                            emote_img = await resp.read()
                    except KeyError:  # let's try Discord
                        await ctx.send(
                            '**No such emote!** I can fetch from Twitch, FrankerFaceZ, BetterTTV, or Discord (soon).'
                        )
                        return False
        img_bytes = io.BytesIO(emote_img)
        ext = imghdr.what(img_bytes)
        await ctx.send(file=discord.File(img_bytes, f'emote.{ext}'))
Example #30
0
def interaction_command_factory(name,
                                *,
                                normal: list,
                                reject: list,
                                condition_rejected: list = author +
                                "could not do this to" + author.reflexive,
                                condition_predicate=lambda _, __: True):
    async def command(self, ctx, *users: RelativeMemberConverter):

        message = await make_response(self, ctx, *users)

        if message is not None:

            async def unload():
                await message.delete()

            ctx.bot.get_cog("Uninvoke").create_unload(ctx.message, unload)

    command.__doc__ = f"executes a {name} action towards selected users, if allowed"

    async def make_response(self, ctx, *users: discord.Member):
        users: List[discord.Member] = list(users)

        if not user_accepts(ctx.author, name, "thing"):
            return await ctx.send(f"But you don't like that")

        if ctx.message.reference is not None:
            referenced: discord.Message
            referenced = (ctx.message.reference.cached_message
                          or await ctx.message.channel.fetch_message(
                              ctx.message.reference.message_id))

            users.append(referenced.author)

        allowed = []
        role_denied = []
        condition_denied = []

        for user in users:
            # noinspection PyTypeChecker
            if user_accepts(user, name, "thing"):
                if condition_predicate(ctx.author, user):
                    allowed.append(user)
                else:
                    condition_denied.append(user)
            else:
                role_denied.append(user)

        if not users:
            return await ctx.send(
                "No users", allowed_mentions=discord.AllowedMentions.none())

        title_baking = []

        if allowed:
            title_baking.extend(normal)

        description_baking = []

        if allowed and (role_denied or condition_denied):
            description_baking.append("however")

        if role_denied:
            description_baking.extend(reject)

        if condition_denied:
            description_baking.extend(condition_rejected)

        with PhraseBuilder() as builder:
            title = await builder.build(title_baking,
                                        speaker=ctx.bot.user,
                                        deferred={
                                            "action_author": ctx.author,
                                            "valid": allowed,
                                            "rejected": role_denied,
                                            "condition": condition_denied
                                        })

            if title == "":
                title = discord.Embed.Empty

            elif len(title) > 256:
                return await ctx.message.reply(
                    embed=discord.Embed(title="(ಠ_ಠ)"),
                    allowed_mentions=discord.AllowedMentions.none())

            description = await builder.build(description_baking,
                                              speaker=ctx.bot.user,
                                              deferred={
                                                  "action_author": ctx.author,
                                                  "valid": allowed,
                                                  "rejected": role_denied,
                                                  "condition": condition_denied
                                              })

            if description == "":
                description = discord.Embed.Empty

        embed = discord.Embed(title=title, description=description)

        return await ctx.send(embed=embed,
                              allowed_mentions=discord.AllowedMentions.none())

    command = commands.guild_only()(commands.Command(command, name=name))

    # inject command into class by putting it into a variable in it's frame
    inspect.currentframe().f_back.f_locals[f"_command_{name}"] = command