โก 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.