โšก COMMAND SYSTEM & DECORATORS CHEAT SHEET

โšก COMMAND SYSTEM & DECORATORS CHEAT SHEET #

Ultra-Condensed Enlightenment Guide


๐ŸŽฎ COMMAND CLASS HIERARCHY #

  • ๐ŸŽฏ Base Classes: Command, Group, HybridCommand for different command patterns
  • ๐Ÿ”ง Command Types: Regular, Group (subcommands), Hybrid (slash + prefix)
  • ๐Ÿ“Š Inheritance: commands.Command โ†’ RedCommand โ†’ Enhanced functionality
  • ๐ŸŽจ Factory Pattern: @commands.command() decorator creates Command instances
  • ๐Ÿ”„ Dynamic Creation: Runtime command creation and registration
  • ๐Ÿ›ก๏ธ Validation: Comprehensive parameter validation at creation
  • ๐Ÿ“ฆ Metadata: Rich metadata storage for help, examples, usage
  • ๐ŸŽฏ Error Handling: Built-in error handling with customizable responses
  • ๐Ÿ”ง Hooks: Before/after invoke hooks for processing pipeline
  • ๐Ÿ“Š Statistics: Command usage tracking and performance metrics

๐Ÿงฉ COMMAND DECORATORS MASTERY #

Core Command Decorators #

@commands.command(name="mycommand", aliases=["mc", "mycmd"])
@commands.guild_only()                    # Guild-only restriction
@commands.dm_only()                       # DM-only restriction
@commands.is_owner()                      # Bot owner only
@commands.admin_or_permissions(manage_guild=True)  # Admin or specific perms
@commands.mod_or_permissions(manage_messages=True) # Mod or specific perms
@commands.has_permissions(kick_members=True)       # Required permissions
@commands.bot_has_permissions(ban_members=True)    # Bot required perms
@commands.cooldown(rate=1, per=30, type=commands.BucketType.user)

Advanced Permission Decorators #

@commands.check(lambda ctx: ctx.author.id in allowed_users)
@commands.check_any(commands.is_owner(), commands.has_role("Admin"))
@commands.has_any_role("Moderator", "Admin", "Staff")
@commands.has_role("VIP")
@commands.bot_has_guild_permissions(manage_roles=True)
@commands.before_invoke(setup_function)
@commands.after_invoke(cleanup_function)

๐Ÿ”ง CONVERTER SYSTEM ARCHITECTURE #

Built-in Converters (20+ Available) #

# Discord Object Converters
discord.Member          # Mentions, names, nicknames, IDs
discord.User            # Global user lookup
discord.TextChannel     # Channel mentions, names, IDs
discord.VoiceChannel    # Voice channel lookup
discord.Role            # Role mentions, names, IDs
discord.Guild           # Guild lookup (bot owner only)
discord.Emoji           # Custom emoji conversion
discord.PartialEmoji    # Any emoji (custom or unicode)
discord.Message         # Message ID or URL conversion
discord.Invite          # Invite code/URL conversion

# Utility Converters
commands.Greedy[Type]   # Consume all remaining args of type
typing.Union[Type1, Type2]  # Try multiple types in order
typing.Optional[Type]   # Optional parameter with None default
commands.Range[int, 1, 100]  # Ranged integer values
commands.Codeblock      # Extract code from codeblocks

Custom Converter Implementation #

class TimeConverter(commands.Converter):
    async def convert(self, ctx, argument):
        # Parse time strings like "1h30m", "2d", "45s"
        match = re.match(r'(\d+)([smhd])', argument.lower())
        if not match:
            raise commands.BadArgument(f"Invalid time format: {argument}")
        
        value, unit = match.groups()
        multipliers = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
        return int(value) * multipliers[unit]

# Usage in command
@commands.command()
async def remind(self, ctx, duration: TimeConverter, *, message):
    # duration is now converted to seconds
    pass

๐ŸŽฏ ARGUMENT PARSING MASTERY #

Parameter Types and Patterns #

# Basic parameter types
async def cmd(self, ctx, user: discord.Member, amount: int):
    pass

# Optional parameters with defaults
async def cmd(self, ctx, user: discord.Member = None, amount: int = 10):
    pass

# Variable arguments
async def cmd(self, ctx, *users: discord.Member):
    # users is tuple of all remaining Member arguments
    pass

# Keyword-only arguments (after *)
async def cmd(self, ctx, user: discord.Member, *, reason="No reason"):
    # reason consumes all remaining text as single string
    pass

# Greedy converters
async def cmd(self, ctx, users: commands.Greedy[discord.Member], amount: int = 1):
    # Consumes as many members as possible, then amount
    pass

# Union types (try in order)
async def cmd(self, ctx, target: typing.Union[discord.Member, discord.User]):
    # Try Member first, fallback to User
    pass

Flag System (Advanced) #

class BanFlags(commands.FlagConverter):
    reason: str = commands.flag(description="Reason for ban")
    days: int = commands.flag(default=1, description="Days of messages to delete")
    silent: bool = commands.flag(default=False, description="Silent ban")

@commands.command()
async def ban(self, ctx, member: discord.Member, *, flags: BanFlags):
    # Usage: !ban @user --reason "Spam" --days 7 --silent
    await member.ban(reason=flags.reason, delete_message_days=flags.days)
    if not flags.silent:
        await ctx.send(f"Banned {member} for {flags.reason}")

๐Ÿ”’ PERMISSION CHECK SYSTEM #

Permission Hierarchy #

# Red's Permission Levels (in order)
PrivilegeLevel.NONE         # No special privileges
PrivilegeLevel.MOD          # Moderator level
PrivilegeLevel.ADMIN        # Administrator level  
PrivilegeLevel.GUILD_OWNER  # Guild owner
PrivilegeLevel.BOT_OWNER    # Bot owner (highest)

# Usage in checks
async def has_mod_perms(ctx):
    return await bot.is_mod(ctx.author)

@commands.check(has_mod_perms)
async def modcommand(self, ctx):
    pass

Custom Permission Checks #

def require_channel(*channel_names):
    async def predicate(ctx):
        return ctx.channel.name in channel_names
    return commands.check(predicate)

def require_user_id(*user_ids):
    async def predicate(ctx):
        return ctx.author.id in user_ids
    return commands.check(predicate)

def require_guild_feature(feature):
    async def predicate(ctx):
        return feature in ctx.guild.features
    return commands.check(predicate)

# Usage
@require_channel("bot-commands", "admin")
@require_guild_feature("COMMUNITY")
async def special_command(self, ctx):
    pass

โฐ COOLDOWN SYSTEM ARCHITECTURE #

Bucket Types and Patterns #

# Cooldown bucket types
commands.BucketType.default     # Global cooldown
commands.BucketType.user        # Per-user cooldown
commands.BucketType.guild       # Per-guild cooldown
commands.BucketType.channel     # Per-channel cooldown
commands.BucketType.member      # Per-member (guild+user) cooldown
commands.BucketType.category    # Per-category cooldown
commands.BucketType.role        # Per-role cooldown

# Multiple cooldowns
@commands.cooldown(1, 30, commands.BucketType.user)        # 1 per 30s per user
@commands.cooldown(5, 60, commands.BucketType.guild)       # 5 per 60s per guild
@commands.max_concurrency(1, commands.BucketType.user)     # Max 1 concurrent per user

# Dynamic cooldowns
@commands.dynamic_cooldown(lambda ctx: get_user_cooldown(ctx.author))
async def dynamic_cmd(self, ctx):
    pass

Cooldown Management #

# Reset cooldowns
command.reset_cooldown(ctx)                    # Reset for context
command.get_cooldown_retry_after(ctx)         # Get remaining time
bucket = command._buckets.get_bucket(ctx.message)  # Get bucket
bucket.reset()                                 # Reset bucket

# Custom cooldown handling
@commands.Cog.listener()
async def on_command_error(self, ctx, error):
    if isinstance(error, commands.CommandOnCooldown):
        await ctx.send(f"Cooldown: {error.retry_after:.1f}s remaining")

๐Ÿ“‹ HELP SYSTEM INTEGRATION #

Help Command Customization #

class CustomHelpCommand(commands.DefaultHelpCommand):
    def __init__(self):
        super().__init__(
            command_attrs={
                "help": "Shows help for commands",
                "cooldown": commands.Cooldown(1, 3, commands.BucketType.user)
            }
        )
    
    async def send_bot_help(self, mapping):
        # Custom bot help implementation
        pass
    
    async def send_cog_help(self, cog):
        # Custom cog help implementation
        pass
    
    async def send_command_help(self, command):
        # Custom command help implementation
        pass

# Set custom help
bot.help_command = CustomHelpCommand()

Help Documentation Patterns #

@commands.command(
    brief="Short description for help list",
    help="Long detailed description with examples",
    usage="<required_arg> [optional_arg]",
    aliases=["alias1", "alias2"]
)
async def example(self, ctx, required_arg, optional_arg="default"):
    """
    Docstring provides additional help information.
    
    Examples:
        !example hello
        !example hello world
    """
    pass

๐ŸŽช COMMAND GROUPS & SUBCOMMANDS #

Group Command Structure #

@commands.group(name="config", invoke_without_command=True)
async def config_group(self, ctx):
    """Main config command - shows help if no subcommand"""
    if ctx.invoked_subcommand is None:
        await ctx.send_help(ctx.command)

@config_group.command(name="set")
async def config_set(self, ctx, setting, value):
    """Subcommand: !config set prefix !"""
    pass

@config_group.command(name="get")
async def config_get(self, ctx, setting):
    """Subcommand: !config get prefix"""
    pass

@config_group.group(name="roles")
async def config_roles(self, ctx):
    """Nested group: !config roles"""
    pass

@config_roles.command(name="add")
async def config_roles_add(self, ctx, role):
    """Nested subcommand: !config roles add @role"""
    pass

๐Ÿ”„ HYBRID COMMANDS (Slash + Prefix) #

@commands.hybrid_command(name="info")
async def info_command(self, ctx, user: discord.Member = None):
    """Works as both !info and /info"""
    user = user or ctx.author
    embed = discord.Embed(title=f"Info for {user}")
    await ctx.send(embed=embed)

@commands.hybrid_group(name="moderation")
async def mod_group(self, ctx):
    """Hybrid group for moderation commands"""
    pass

@mod_group.command(name="ban")
async def ban_hybrid(self, ctx, member: discord.Member, *, reason="No reason"):
    """Hybrid ban command: !moderation ban or /moderation ban"""
    await member.ban(reason=reason)
    await ctx.send(f"Banned {member}")

๐Ÿ› ๏ธ COMMAND LIFECYCLE HOOKS #

async def command_before_invoke(self, ctx):
    """Runs before every command in this cog"""
    print(f"Command {ctx.command} invoked by {ctx.author}")
    # Log command usage
    await self.log_command_usage(ctx)

async def command_after_invoke(self, ctx):
    """Runs after every command in this cog"""
    print(f"Command {ctx.command} completed")
    # Update command statistics
    await self.update_command_stats(ctx)

@commands.before_invoke(command_before_invoke)
@commands.after_invoke(command_after_invoke)
@commands.command()
async def tracked_command(self, ctx):
    """Command with before/after hooks"""
    await ctx.send("Command executed!")

# Global hooks (in cog)
async def cog_before_invoke(self, ctx):
    """Runs before any command in this cog"""
    pass

async def cog_after_invoke(self, ctx):
    """Runs after any command in this cog"""  
    pass

๐Ÿ” ERROR HANDLING MASTERY #

@commands.Cog.listener()
async def on_command_error(self, ctx, error):
    """Comprehensive error handling"""
    if isinstance(error, commands.CommandNotFound):
        return  # Ignore command not found
    
    elif isinstance(error, commands.MissingRequiredArgument):
        await ctx.send(f"Missing argument: {error.param}")
        await ctx.send_help(ctx.command)
    
    elif isinstance(error, commands.BadArgument):
        await ctx.send(f"Invalid argument: {error}")
        await ctx.send_help(ctx.command)
    
    elif isinstance(error, commands.CheckFailure):
        await ctx.send("You don't have permission to use this command")
    
    elif isinstance(error, commands.CommandOnCooldown):
        await ctx.send(f"Cooldown: {error.retry_after:.1f}s remaining")
    
    elif isinstance(error, commands.MaxConcurrencyReached):
        await ctx.send("Command is already running. Please wait.")
    
    else:
        # Log unexpected errors
        print(f"Unexpected error in {ctx.command}: {error}")

โšก QUICK COMMAND REFERENCE #

# Basic Command
@commands.command(name="ping", aliases=["p"])
async def ping(self, ctx):
    await ctx.send("Pong!")

# Command with Arguments
@commands.command()
async def say(self, ctx, *, message):
    await ctx.send(message)

# Permission Check
@commands.has_permissions(manage_messages=True)
@commands.command()
async def clear(self, ctx, amount: int = 10):
    await ctx.channel.purge(limit=amount)

# Cooldown
@commands.cooldown(1, 30, commands.BucketType.user)
@commands.command()
async def daily(self, ctx):
    # Give daily reward
    pass

# Group with Subcommands
@commands.group()
async def settings(self, ctx):
    if ctx.invoked_subcommand is None:
        await ctx.send_help(ctx.command)

@settings.command()
async def prefix(self, ctx, new_prefix):
    # Set new prefix
    pass

โšก Commands are the interface between users and your bot. Master this system for intuitive user experiences.