A modern, feature-rich, and async-ready API wrapper for Discord written in Python
—
Comprehensive event system for responding to Discord activities including messages, reactions, member changes, guild updates, and voice state changes. Discord.py provides both high-level events and raw gateway events for detailed control over bot behavior.
Essential events for bot functionality and user interactions.
@client.event
async def on_ready():
"""
Called when the client has successfully connected to Discord.
Bot is fully ready to process events and commands.
"""
pass
@client.event
async def on_resumed():
"""
Called when the client has resumed a session.
"""
pass
@client.event
async def on_connect():
"""
Called when the client has connected to Discord's gateway.
This may be called multiple times during reconnections.
"""
pass
@client.event
async def on_disconnect():
"""
Called when the client has disconnected from Discord's gateway.
"""
pass
@client.event
async def on_shard_connect(shard_id: int):
"""
Called when a shard connects to Discord.
Parameters:
- shard_id: ID of the connected shard
"""
pass
@client.event
async def on_shard_disconnect(shard_id: int):
"""
Called when a shard disconnects from Discord.
Parameters:
- shard_id: ID of the disconnected shard
"""
pass
@client.event
async def on_shard_ready(shard_id: int):
"""
Called when a shard becomes ready.
Parameters:
- shard_id: ID of the ready shard
"""
pass
@client.event
async def on_shard_resumed(shard_id: int):
"""
Called when a shard resumes a session.
Parameters:
- shard_id: ID of the resumed shard
"""
passEvents for message creation, editing, deletion, and reactions.
@client.event
async def on_message(message: Message):
"""
Called when a message is created.
Parameters:
- message: The message that was created
"""
pass
@client.event
async def on_message_edit(before: Message, after: Message):
"""
Called when a message is edited.
Parameters:
- before: Message before editing
- after: Message after editing
"""
pass
@client.event
async def on_message_delete(message: Message):
"""
Called when a message is deleted.
Parameters:
- message: The deleted message (may be partial)
"""
pass
@client.event
async def on_bulk_message_delete(messages: List[Message]):
"""
Called when multiple messages are deleted at once.
Parameters:
- messages: List of deleted messages
"""
pass
@client.event
async def on_reaction_add(reaction: Reaction, user: Union[User, Member]):
"""
Called when a reaction is added to a message.
Parameters:
- reaction: The reaction that was added
- user: User who added the reaction
"""
pass
@client.event
async def on_reaction_remove(reaction: Reaction, user: Union[User, Member]):
"""
Called when a reaction is removed from a message.
Parameters:
- reaction: The reaction that was removed
- user: User who removed the reaction
"""
pass
@client.event
async def on_reaction_clear(message: Message, reactions: List[Reaction]):
"""
Called when all reactions are cleared from a message.
Parameters:
- message: Message that had reactions cleared
- reactions: List of reactions that were cleared
"""
pass
@client.event
async def on_reaction_clear_emoji(reaction: Reaction):
"""
Called when all reactions of a specific emoji are cleared.
Parameters:
- reaction: The reaction that was cleared
"""
pass
@client.event
async def on_typing(channel: Messageable, user: Union[User, Member], when: datetime):
"""
Called when someone starts typing.
Parameters:
- channel: Channel where typing started
- user: User who started typing
- when: When typing started
"""
passEvents for guild (server) management and changes.
@client.event
async def on_guild_join(guild: Guild):
"""
Called when the bot joins a guild.
Parameters:
- guild: Guild that was joined
"""
pass
@client.event
async def on_guild_remove(guild: Guild):
"""
Called when the bot is removed from a guild.
Parameters:
- guild: Guild that was left
"""
pass
@client.event
async def on_guild_update(before: Guild, after: Guild):
"""
Called when a guild is updated.
Parameters:
- before: Guild before update
- after: Guild after update
"""
pass
@client.event
async def on_guild_available(guild: Guild):
"""
Called when a guild becomes available.
Parameters:
- guild: Guild that became available
"""
pass
@client.event
async def on_guild_unavailable(guild: Guild):
"""
Called when a guild becomes unavailable.
Parameters:
- guild: Guild that became unavailable
"""
pass
@client.event
async def on_guild_channel_create(channel: GuildChannel):
"""
Called when a guild channel is created.
Parameters:
- channel: Channel that was created
"""
pass
@client.event
async def on_guild_channel_delete(channel: GuildChannel):
"""
Called when a guild channel is deleted.
Parameters:
- channel: Channel that was deleted
"""
pass
@client.event
async def on_guild_channel_update(before: GuildChannel, after: GuildChannel):
"""
Called when a guild channel is updated.
Parameters:
- before: Channel before update
- after: Channel after update
"""
pass
@client.event
async def on_guild_channel_pins_update(channel: Union[TextChannel, DMChannel, GroupChannel], last_pin: Optional[datetime]):
"""
Called when channel pins are updated.
Parameters:
- channel: Channel with pin update
- last_pin: Timestamp of most recent pin
"""
pass
@client.event
async def on_guild_integrations_update(guild: Guild):
"""
Called when guild integrations are updated.
Parameters:
- guild: Guild with integration update
"""
pass
@client.event
async def on_webhooks_update(channel: TextChannel):
"""
Called when channel webhooks are updated.
Parameters:
- channel: Channel with webhook update
"""
pass
@client.event
async def on_guild_role_create(role: Role):
"""
Called when a guild role is created.
Parameters:
- role: Role that was created
"""
pass
@client.event
async def on_guild_role_delete(role: Role):
"""
Called when a guild role is deleted.
Parameters:
- role: Role that was deleted
"""
pass
@client.event
async def on_guild_role_update(before: Role, after: Role):
"""
Called when a guild role is updated.
Parameters:
- before: Role before update
- after: Role after update
"""
pass
@client.event
async def on_guild_emojis_update(guild: Guild, before: List[Emoji], after: List[Emoji]):
"""
Called when guild emojis are updated.
Parameters:
- guild: Guild with emoji update
- before: Emojis before update
- after: Emojis after update
"""
pass
@client.event
async def on_guild_stickers_update(guild: Guild, before: List[GuildSticker], after: List[GuildSticker]):
"""
Called when guild stickers are updated.
Parameters:
- guild: Guild with sticker update
- before: Stickers before update
- after: Stickers after update
"""
passEvents for member management, joins, leaves, and updates.
@client.event
async def on_member_join(member: Member):
"""
Called when a member joins a guild.
Parameters:
- member: Member who joined
"""
pass
@client.event
async def on_member_remove(member: Member):
"""
Called when a member leaves a guild.
Parameters:
- member: Member who left
"""
pass
@client.event
async def on_member_update(before: Member, after: Member):
"""
Called when a member is updated.
Parameters:
- before: Member before update
- after: Member after update
"""
pass
@client.event
async def on_user_update(before: User, after: User):
"""
Called when a user updates their profile.
Parameters:
- before: User before update
- after: User after update
"""
pass
@client.event
async def on_member_ban(guild: Guild, user: Union[User, Member]):
"""
Called when a member is banned from a guild.
Parameters:
- guild: Guild where ban occurred
- user: User who was banned
"""
pass
@client.event
async def on_member_unban(guild: Guild, user: User):
"""
Called when a member is unbanned from a guild.
Parameters:
- guild: Guild where unban occurred
- user: User who was unbanned
"""
pass
@client.event
async def on_presence_update(before: Member, after: Member):
"""
Called when a member's presence is updated.
Requires PRESENCE_UPDATE intent.
Parameters:
- before: Member before presence update
- after: Member after presence update
"""
passEvents for voice channel activity and state changes.
@client.event
async def on_voice_state_update(member: Member, before: VoiceState, after: VoiceState):
"""
Called when a member's voice state changes.
Parameters:
- member: Member whose voice state changed
- before: Voice state before change
- after: Voice state after change
"""
pass
@client.event
async def on_stage_instance_create(stage_instance: StageInstance):
"""
Called when a stage instance is created.
Parameters:
- stage_instance: Stage instance that was created
"""
pass
@client.event
async def on_stage_instance_delete(stage_instance: StageInstance):
"""
Called when a stage instance is deleted.
Parameters:
- stage_instance: Stage instance that was deleted
"""
pass
@client.event
async def on_stage_instance_update(before: StageInstance, after: StageInstance):
"""
Called when a stage instance is updated.
Parameters:
- before: Stage instance before update
- after: Stage instance after update
"""
passEvents for slash commands, buttons, select menus, and other interactions.
@client.event
async def on_interaction(interaction: Interaction):
"""
Called when an interaction is received.
Parameters:
- interaction: The interaction that was received
"""
pass
@client.event
async def on_application_command_error(interaction: Interaction, error: AppCommandError):
"""
Called when an application command raises an error.
Parameters:
- interaction: Interaction that caused the error
- error: Error that was raised
"""
pass
@client.event
async def on_app_command_completion(interaction: Interaction, command: Union[Command, ContextMenu]):
"""
Called when an application command completes successfully.
Parameters:
- interaction: Interaction that completed
- command: Command that was executed
"""
passEvents for thread creation, deletion, and management.
@client.event
async def on_thread_create(thread: Thread):
"""
Called when a thread is created.
Parameters:
- thread: Thread that was created
"""
pass
@client.event
async def on_thread_delete(thread: Thread):
"""
Called when a thread is deleted.
Parameters:
- thread: Thread that was deleted
"""
pass
@client.event
async def on_thread_update(before: Thread, after: Thread):
"""
Called when a thread is updated.
Parameters:
- before: Thread before update
- after: Thread after update
"""
pass
@client.event
async def on_thread_member_join(member: ThreadMember):
"""
Called when a member joins a thread.
Parameters:
- member: Thread member who joined
"""
pass
@client.event
async def on_thread_member_remove(member: ThreadMember):
"""
Called when a member leaves a thread.
Parameters:
- member: Thread member who left
"""
pass
@client.event
async def on_thread_join(thread: Thread):
"""
Called when the bot joins a thread.
Parameters:
- thread: Thread that was joined
"""
pass
@client.event
async def on_thread_remove(thread: Thread):
"""
Called when the bot is removed from a thread.
Parameters:
- thread: Thread that was left
"""
passEvents for guild scheduled events.
@client.event
async def on_scheduled_event_create(event: ScheduledEvent):
"""
Called when a scheduled event is created.
Parameters:
- event: Scheduled event that was created
"""
pass
@client.event
async def on_scheduled_event_delete(event: ScheduledEvent):
"""
Called when a scheduled event is deleted.
Parameters:
- event: Scheduled event that was deleted
"""
pass
@client.event
async def on_scheduled_event_update(before: ScheduledEvent, after: ScheduledEvent):
"""
Called when a scheduled event is updated.
Parameters:
- before: Event before update
- after: Event after update
"""
pass
@client.event
async def on_scheduled_event_user_add(event: ScheduledEvent, user: User):
"""
Called when a user subscribes to a scheduled event.
Parameters:
- event: Scheduled event
- user: User who subscribed
"""
pass
@client.event
async def on_scheduled_event_user_remove(event: ScheduledEvent, user: User):
"""
Called when a user unsubscribes from a scheduled event.
Parameters:
- event: Scheduled event
- user: User who unsubscribed
"""
passEvents for Discord's auto-moderation system.
@client.event
async def on_automod_rule_create(rule: AutoModRule):
"""
Called when an auto-moderation rule is created.
Parameters:
- rule: Auto-moderation rule that was created
"""
pass
@client.event
async def on_automod_rule_delete(rule: AutoModRule):
"""
Called when an auto-moderation rule is deleted.
Parameters:
- rule: Auto-moderation rule that was deleted
"""
pass
@client.event
async def on_automod_rule_update(before: AutoModRule, after: AutoModRule):
"""
Called when an auto-moderation rule is updated.
Parameters:
- before: Rule before update
- after: Rule after update
"""
pass
@client.event
async def on_automod_action(execution: AutoModAction):
"""
Called when an auto-moderation action is executed.
Parameters:
- execution: Auto-moderation action execution
"""
passLow-level events providing raw Discord gateway data.
@client.event
async def on_raw_message_edit(payload: RawMessageUpdateEvent):
"""
Called when a message is edited (raw event).
Parameters:
- payload: Raw message update data
"""
pass
@client.event
async def on_raw_message_delete(payload: RawMessageDeleteEvent):
"""
Called when a message is deleted (raw event).
Parameters:
- payload: Raw message delete data
"""
pass
@client.event
async def on_raw_bulk_message_delete(payload: RawBulkMessageDeleteEvent):
"""
Called when messages are bulk deleted (raw event).
Parameters:
- payload: Raw bulk delete data
"""
pass
@client.event
async def on_raw_reaction_add(payload: RawReactionActionEvent):
"""
Called when a reaction is added (raw event).
Parameters:
- payload: Raw reaction data
"""
pass
@client.event
async def on_raw_reaction_remove(payload: RawReactionActionEvent):
"""
Called when a reaction is removed (raw event).
Parameters:
- payload: Raw reaction data
"""
pass
@client.event
async def on_raw_reaction_clear(payload: RawReactionClearEvent):
"""
Called when reactions are cleared (raw event).
Parameters:
- payload: Raw reaction clear data
"""
pass
@client.event
async def on_raw_reaction_clear_emoji(payload: RawReactionClearEmojiEvent):
"""
Called when emoji reactions are cleared (raw event).
Parameters:
- payload: Raw emoji clear data
"""
pass
@client.event
async def on_raw_typing(payload: RawTypingEvent):
"""
Called when typing starts (raw event).
Parameters:
- payload: Raw typing data
"""
pass
@client.event
async def on_raw_member_remove(payload: RawMemberRemoveEvent):
"""
Called when a member leaves (raw event).
Parameters:
- payload: Raw member remove data
"""
pass
@client.event
async def on_raw_thread_update(payload: RawThreadUpdateEvent):
"""
Called when a thread is updated (raw event).
Parameters:
- payload: Raw thread update data
"""
pass
@client.event
async def on_raw_thread_delete(payload: RawThreadDeleteEvent):
"""
Called when a thread is deleted (raw event).
Parameters:
- payload: Raw thread delete data
"""
pass
@client.event
async def on_raw_app_command_permissions_update(payload: RawAppCommandPermissionsUpdateEvent):
"""
Called when app command permissions are updated (raw event).
Parameters:
- payload: Raw permissions update data
"""
passEvents for handling errors and exceptions.
@client.event
async def on_error(event: str, *args, **kwargs):
"""
Called when an event handler raises an uncaught exception.
Parameters:
- event: Name of the event that raised the exception
- args: Positional arguments passed to the event
- kwargs: Keyword arguments passed to the event
"""
pass
@client.event
async def on_command_error(ctx: Context, error: CommandError):
"""
Called when a command raises an error.
Only available when using the commands extension.
Parameters:
- ctx: Command context
- error: Error that was raised
"""
passRaw event payload classes for low-level event handling:
class RawMessageUpdateEvent:
"""Raw message update event payload."""
message_id: int # Message ID
channel_id: int # Channel ID
guild_id: Optional[int] # Guild ID
data: Dict[str, Any] # Message data
cached_message: Optional[Message] # Cached message if available
class RawMessageDeleteEvent:
"""Raw message delete event payload."""
message_id: int # Deleted message ID
channel_id: int # Channel ID
guild_id: Optional[int] # Guild ID
cached_message: Optional[Message] # Cached message if available
class RawBulkMessageDeleteEvent:
"""Raw bulk message delete event payload."""
message_ids: Set[int] # Deleted message IDs
channel_id: int # Channel ID
guild_id: Optional[int] # Guild ID
cached_messages: List[Message] # Cached messages if available
class RawReactionActionEvent:
"""Raw reaction add/remove event payload."""
message_id: int # Message ID
user_id: int # User ID
channel_id: int # Channel ID
guild_id: Optional[int] # Guild ID
emoji: PartialEmoji # Reaction emoji
member: Optional[Member] # Member who reacted (if available)
event_type: str # 'REACTION_ADD' or 'REACTION_REMOVE'
class RawReactionClearEvent:
"""Raw reaction clear event payload."""
message_id: int # Message ID
channel_id: int # Channel ID
guild_id: Optional[int] # Guild ID
class RawReactionClearEmojiEvent:
"""Raw emoji reaction clear event payload."""
message_id: int # Message ID
channel_id: int # Channel ID
guild_id: Optional[int] # Guild ID
emoji: PartialEmoji # Cleared emoji
class RawTypingEvent:
"""Raw typing event payload."""
channel_id: int # Channel ID
user_id: int # User ID
timestamp: datetime # When typing started
guild_id: Optional[int] # Guild ID
member: Optional[Member] # Member who is typing
class RawMemberRemoveEvent:
"""Raw member remove event payload."""
user: User # User who left
guild_id: int # Guild ID
class RawThreadUpdateEvent:
"""Raw thread update event payload."""
thread_id: int # Thread ID
guild_id: int # Guild ID
data: Dict[str, Any] # Thread data
thread: Optional[Thread] # Thread object if cached
class RawThreadDeleteEvent:
"""Raw thread delete event payload."""
thread_id: int # Thread ID
guild_id: int # Guild ID
parent_id: int # Parent channel ID
thread_type: ChannelType # Thread type
thread: Optional[Thread] # Thread object if cached
class RawAppCommandPermissionsUpdateEvent:
"""Raw app command permissions update event payload."""
id: int # Command ID
application_id: int # Application ID
guild_id: int # Guild ID
permissions: List[AppCommandPermissions] # Updated permissionsimport discord
from discord.ext import commands
bot = commands.Bot(command_prefix='!', intents=discord.Intents.all())
@bot.event
async def on_ready():
print(f'{bot.user} has connected to Discord!')
print(f'Connected to {len(bot.guilds)} guilds')
@bot.event
async def on_member_join(member):
# Send welcome message
channel = discord.utils.get(member.guild.text_channels, name='welcome')
if channel:
embed = discord.Embed(
title=f'Welcome {member.display_name}!',
description=f'Welcome to {member.guild.name}!',
color=0x00ff00
)
embed.set_thumbnail(url=member.display_avatar.url)
embed.add_field(name='Member Count', value=len(member.guild.members))
await channel.send(embed=embed)
@bot.event
async def on_member_remove(member):
# Log member leave
channel = discord.utils.get(member.guild.text_channels, name='mod-log')
if channel:
embed = discord.Embed(
title='Member Left',
description=f'{member} left the server',
color=0xff0000,
timestamp=discord.utils.utcnow()
)
await channel.send(embed=embed)
@bot.event
async def on_message(message):
# Ignore bot messages
if message.author.bot:
return
# Auto-react to messages containing "discord.py"
if 'discord.py' in message.content.lower():
await message.add_reaction('🐍')
# Process commands
await bot.process_commands(message)
bot.run('YOUR_TOKEN')@bot.event
async def on_raw_reaction_add(payload):
"""Handle reaction role assignment."""
# Configuration: message_id -> {emoji: role_id}
REACTION_ROLES = {
123456789012345678: { # Replace with actual message ID
'🎮': 987654321098765432, # Gaming role ID
'🎵': 876543210987654321, # Music role ID
'📚': 765432109876543210, # Study role ID
}
}
if payload.message_id not in REACTION_ROLES:
return
if payload.user_id == bot.user.id:
return # Ignore bot reactions
guild = bot.get_guild(payload.guild_id)
if not guild:
return
member = guild.get_member(payload.user_id)
if not member:
return
emoji_str = str(payload.emoji)
role_id = REACTION_ROLES[payload.message_id].get(emoji_str)
if role_id:
role = guild.get_role(role_id)
if role and role not in member.roles:
try:
await member.add_roles(role, reason="Reaction role")
print(f"Added {role.name} to {member}")
except discord.HTTPException as e:
print(f"Failed to add role: {e}")
@bot.event
async def on_raw_reaction_remove(payload):
"""Handle reaction role removal."""
# Same configuration as above
REACTION_ROLES = {
123456789012345678: {
'🎮': 987654321098765432,
'🎵': 876543210987654321,
'📚': 765432109876543210,
}
}
if payload.message_id not in REACTION_ROLES:
return
if payload.user_id == bot.user.id:
return
guild = bot.get_guild(payload.guild_id)
if not guild:
return
member = guild.get_member(payload.user_id)
if not member:
return
emoji_str = str(payload.emoji)
role_id = REACTION_ROLES[payload.message_id].get(emoji_str)
if role_id:
role = guild.get_role(role_id)
if role and role in member.roles:
try:
await member.remove_roles(role, reason="Reaction role removed")
print(f"Removed {role.name} from {member}")
except discord.HTTPException as e:
print(f"Failed to remove role: {e}")@bot.event
async def on_message_edit(before, after):
"""Log message edits."""
if before.author.bot:
return
if before.content == after.content:
return # No content change
# Log to mod channel
mod_channel = discord.utils.get(before.guild.text_channels, name='mod-log')
if mod_channel:
embed = discord.Embed(
title='Message Edited',
color=0xffaa00,
timestamp=after.edited_at
)
embed.add_field(name='Author', value=before.author.mention, inline=True)
embed.add_field(name='Channel', value=before.channel.mention, inline=True)
embed.add_field(name='Message ID', value=before.id, inline=True)
embed.add_field(name='Before', value=before.content[:1024], inline=False)
embed.add_field(name='After', value=after.content[:1024], inline=False)
embed.add_field(name='Jump to Message', value=f'[Click here]({after.jump_url})', inline=False)
await mod_channel.send(embed=embed)
@bot.event
async def on_message_delete(message):
"""Log message deletions."""
if message.author.bot:
return
mod_channel = discord.utils.get(message.guild.text_channels, name='mod-log')
if mod_channel:
embed = discord.Embed(
title='Message Deleted',
color=0xff0000,
timestamp=discord.utils.utcnow()
)
embed.add_field(name='Author', value=message.author.mention, inline=True)
embed.add_field(name='Channel', value=message.channel.mention, inline=True)
embed.add_field(name='Message ID', value=message.id, inline=True)
embed.add_field(name='Content', value=message.content[:1024] or '*No content*', inline=False)
if message.attachments:
attachments = '\n'.join([att.filename for att in message.attachments])
embed.add_field(name='Attachments', value=attachments, inline=False)
await mod_channel.send(embed=embed)@bot.event
async def on_voice_state_update(member, before, after):
"""Monitor voice activity."""
# Member joined voice
if before.channel is None and after.channel is not None:
print(f"{member} joined {after.channel}")
# Create temporary voice channel if joining "Create Channel" channel
if after.channel.name == "➕ Create Channel":
category = after.channel.category
temp_channel = await after.channel.guild.create_voice_channel(
name=f"{member.display_name}'s Channel",
category=category,
user_limit=10
)
await member.move_to(temp_channel)
# Set permissions for channel owner
await temp_channel.set_permissions(member, manage_channels=True, manage_permissions=True)
# Member left voice
elif before.channel is not None and after.channel is None:
print(f"{member} left {before.channel}")
# Delete temporary channels when empty
if before.channel.name.endswith("'s Channel") and len(before.channel.members) == 0:
await before.channel.delete(reason="Temporary channel cleanup")
# Member moved channels
elif before.channel != after.channel and before.channel is not None and after.channel is not None:
print(f"{member} moved from {before.channel} to {after.channel}")
# Voice state changes
if before.self_mute != after.self_mute:
status = "muted" if after.self_mute else "unmuted"
print(f"{member} {status} themselves")
if before.self_deaf != after.self_deaf:
status = "deafened" if after.self_deaf else "undeafened"
print(f"{member} {status} themselves")@bot.event
async def on_error(event, *args, **kwargs):
"""Handle errors in event handlers."""
import traceback
import sys
print(f'Ignoring exception in {event}:', file=sys.stderr)
traceback.print_exc()
# Log to error channel
error_channel = bot.get_channel(ERROR_CHANNEL_ID)
if error_channel:
embed = discord.Embed(
title='Event Error',
description=f'Error in event: `{event}`',
color=0xff0000,
timestamp=discord.utils.utcnow()
)
embed.add_field(name='Traceback', value=f'```py\n{traceback.format_exc()[:1900]}\n```', inline=False)
await error_channel.send(embed=embed)
@bot.event
async def on_command_error(ctx, error):
"""Handle command errors."""
if isinstance(error, commands.CommandNotFound):
return # Ignore unknown commands
elif isinstance(error, commands.MissingRequiredArgument):
await ctx.send(f'Missing required argument: `{error.param.name}`')
elif isinstance(error, commands.BadArgument):
await ctx.send(f'Invalid argument: {error}')
elif isinstance(error, commands.MissingPermissions):
perms = ', '.join(error.missing_permissions)
await ctx.send(f'You need the following permissions: {perms}')
elif isinstance(error, commands.BotMissingPermissions):
perms = ', '.join(error.missing_permissions)
await ctx.send(f'I need the following permissions: {perms}')
elif isinstance(error, commands.CommandOnCooldown):
await ctx.send(f'Command is on cooldown. Try again in {error.retry_after:.2f} seconds.')
else:
# Log unexpected errors
import traceback
traceback.print_exception(type(error), error, error.__traceback__)
await ctx.send('An unexpected error occurred.')Install with Tessl CLI
npx tessl i tessl/pypi-discord-py