CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-discord-py-interactions

A Feature-rich Discord Bot Framework for Python with comprehensive API coverage and modern interfaces

Pending
Overview
Eval results
Files

events.mddocs/

Events & Listeners

Comprehensive event system covering Discord gateway events and internal bot events.

Event System Overview

The interactions library provides a rich event system with two main categories:

  • Discord Events: Gateway events from Discord (messages, guilds, users, etc.)
  • Internal Events: Bot lifecycle and framework events (startup, command completion, errors, etc.)

Listening to Events

Basic Event Listener

from interactions import listen, Client, events

bot = Client(token="TOKEN")

@listen()
async def on_message_create(event: events.MessageCreate):
    """Listen to all message create events"""
    message = event.message
    print(f"Message from {message.author}: {message.content}")

@listen(events.GuildJoin)
async def on_guild_join(event: events.GuildJoin):
    """Listen to guild join events"""
    guild = event.guild
    print(f"Joined guild: {guild.name} ({guild.id})")

Multiple Event Types

@listen(events.MessageCreate, events.MessageUpdate)
async def on_message_event(event):
    """Listen to both message create and update events"""
    if isinstance(event, events.MessageCreate):
        print(f"New message: {event.message.content}")
    elif isinstance(event, events.MessageUpdate):
        print(f"Message edited: {event.after.content}")

Class-Based Listeners

from interactions import Extension, listen

class EventExtension(Extension):
    @listen()
    async def on_ready(self, event: events.Ready):
        """Extension-based event listener"""
        print(f"Bot ready! Logged in as {self.bot.user}")
        
    @listen(events.MemberAdd)
    async def on_member_join(self, event: events.MemberAdd):
        """Welcome new members"""
        member = event.member
        channel = member.guild.system_channel
        if channel:
            await channel.send(f"Welcome {member.mention} to {member.guild.name}!")

Discord Gateway Events

Connection Events

@listen(events.Ready)
async def on_ready(event: events.Ready):
    """Bot is ready and connected"""
    user = event.user
    guilds = len(event.guilds)
    print(f"Ready! Logged in as {user} in {guilds} guilds")

@listen(events.Connect)  
async def on_connect(event: events.Connect):
    """Bot connected to gateway"""
    print("Connected to Discord gateway")

@listen(events.Disconnect)
async def on_disconnect(event: events.Disconnect):
    """Bot disconnected from gateway"""
    print("Disconnected from Discord gateway")

@listen(events.Resume)
async def on_resume(event: events.Resume):
    """Gateway connection resumed"""
    print("Gateway connection resumed")

Guild Events

@listen(events.GuildJoin)
async def on_guild_join(event: events.GuildJoin):
    """Bot joins a new guild"""
    guild = event.guild
    print(f"Joined guild: {guild.name} with {guild.member_count} members")

@listen(events.GuildLeft)
async def on_guild_leave(event: events.GuildLeft):
    """Bot leaves/gets kicked from guild"""
    guild = event.guild
    print(f"Left guild: {guild.name}")

@listen(events.GuildUpdate)
async def on_guild_update(event: events.GuildUpdate):
    """Guild settings updated"""
    before = event.before
    after = event.after
    print(f"Guild {after.name} was updated")

@listen(events.GuildAvailable)
async def on_guild_available(event: events.GuildAvailable):
    """Guild becomes available after outage"""
    guild = event.guild
    print(f"Guild {guild.name} is now available")

@listen(events.GuildUnavailable)
async def on_guild_unavailable(event: events.GuildUnavailable):
    """Guild becomes unavailable due to outage"""
    guild = event.guild
    print(f"Guild {guild.name} is now unavailable")

Member Events

@listen(events.MemberAdd)
async def on_member_join(event: events.MemberAdd):
    """New member joins guild"""
    member = event.member
    guild = member.guild
    
    # Send welcome message
    if guild.system_channel:
        embed = Embed(
            title="Welcome!",
            description=f"Welcome {member.mention} to {guild.name}!",
            color=0x00ff00
        )
        await guild.system_channel.send(embed=embed)

@listen(events.MemberRemove)
async def on_member_leave(event: events.MemberRemove):
    """Member leaves guild"""
    member = event.member
    print(f"{member} left {member.guild.name}")

@listen(events.MemberUpdate)
async def on_member_update(event: events.MemberUpdate):
    """Member profile updated (roles, nick, etc.)"""
    before = event.before
    after = event.after
    
    # Check if roles changed
    if before.roles != after.roles:
        added_roles = set(after.roles) - set(before.roles)
        removed_roles = set(before.roles) - set(after.roles)
        
        for role in added_roles:
            print(f"{after} gained role: {role.name}")
        for role in removed_roles:
            print(f"{after} lost role: {role.name}")

Message Events

@listen(events.MessageCreate)
async def on_message(event: events.MessageCreate):
    """New message sent"""
    message = event.message
    
    # Ignore bot messages
    if message.author.bot:
        return
        
    # Auto-react to messages with "python" 
    if "python" in message.content.lower():
        await message.add_reaction("🐍")

@listen(events.MessageUpdate)
async def on_message_edit(event: events.MessageUpdate):
    """Message was edited"""
    before = event.before
    after = event.after
    
    if before and after and before.content != after.content:
        print(f"Message edited in #{after.channel.name}")
        print(f"Before: {before.content}")
        print(f"After: {after.content}")

@listen(events.MessageDelete)
async def on_message_delete(event: events.MessageDelete):
    """Message was deleted"""
    message = event.message
    print(f"Message deleted in #{message.channel.name}: {message.content}")

@listen(events.MessageDeleteBulk)
async def on_bulk_delete(event: events.MessageDeleteBulk):
    """Multiple messages deleted"""
    messages = event.messages
    channel = messages[0].channel if messages else None
    print(f"Bulk deleted {len(messages)} messages in #{channel.name}")

Reaction Events

@listen(events.MessageReactionAdd)
async def on_reaction_add(event: events.MessageReactionAdd):
    """User adds reaction to message"""
    reaction = event.reaction
    user = event.user
    message = event.message
    
    # Role reaction system example
    if str(reaction.emoji) == "👍" and not user.bot:
        role = message.guild.get_role(THUMBS_UP_ROLE_ID)
        if role:
            await user.add_role(role)

@listen(events.MessageReactionRemove)  
async def on_reaction_remove(event: events.MessageReactionRemove):
    """User removes reaction from message"""
    reaction = event.reaction
    user = event.user
    
    if str(reaction.emoji) == "👍" and not user.bot:
        role = event.message.guild.get_role(THUMBS_UP_ROLE_ID)
        if role:
            await user.remove_role(role)

@listen(events.MessageReactionRemoveAll)
async def on_reactions_clear(event: events.MessageReactionRemoveAll):
    """All reactions removed from message"""
    message = event.message
    print(f"All reactions cleared from message in #{message.channel.name}")

Channel Events

@listen(events.ChannelCreate)
async def on_channel_create(event: events.ChannelCreate):
    """New channel created"""
    channel = event.channel
    print(f"New channel created: #{channel.name}")

@listen(events.ChannelUpdate)
async def on_channel_update(event: events.ChannelUpdate):
    """Channel settings updated"""
    before = event.before
    after = event.after
    print(f"Channel #{after.name} was updated")

@listen(events.ChannelDelete)
async def on_channel_delete(event: events.ChannelDelete):
    """Channel deleted"""
    channel = event.channel
    print(f"Channel deleted: #{channel.name}")

@listen(events.ChannelPinsUpdate)
async def on_pins_update(event: events.ChannelPinsUpdate):
    """Channel pins updated"""
    channel = event.channel
    print(f"Pins updated in #{channel.name}")

Thread Events

@listen(events.ThreadCreate)
async def on_thread_create(event: events.ThreadCreate):
    """New thread created"""
    thread = event.thread
    print(f"Thread created: {thread.name}")

@listen(events.ThreadUpdate)
async def on_thread_update(event: events.ThreadUpdate):
    """Thread updated"""
    before = event.before
    after = event.after
    print(f"Thread updated: {after.name}")

@listen(events.ThreadDelete)
async def on_thread_delete(event: events.ThreadDelete):
    """Thread deleted"""
    thread = event.thread
    print(f"Thread deleted: {thread.name}")

@listen(events.ThreadMemberUpdate)
async def on_thread_member_update(event: events.ThreadMemberUpdate):
    """User's thread membership updated"""
    member = event.member
    thread = event.thread
    print(f"{member.user} updated in thread {thread.name}")

Voice Events

@listen(events.VoiceStateUpdate)
async def on_voice_state_update(event: events.VoiceStateUpdate):
    """User's voice state changed"""
    before = event.before
    after = event.after
    member = after.member if after else before.member
    
    # User joined voice channel
    if not before.channel and after.channel:
        print(f"{member} joined voice channel: {after.channel.name}")
    
    # User left voice channel  
    elif before.channel and not after.channel:
        print(f"{member} left voice channel: {before.channel.name}")
    
    # User moved between channels
    elif before.channel != after.channel:
        print(f"{member} moved from {before.channel.name} to {after.channel.name}")

# Convenience voice events
@listen(events.VoiceUserJoin)
async def on_voice_join(event: events.VoiceUserJoin):
    """User joins voice channel (convenience event)"""
    member = event.member
    channel = event.channel
    print(f"{member} joined {channel.name}")

@listen(events.VoiceUserLeave)
async def on_voice_leave(event: events.VoiceUserLeave):
    """User leaves voice channel (convenience event)"""
    member = event.member
    channel = event.channel  
    print(f"{member} left {channel.name}")

@listen(events.VoiceUserMove)
async def on_voice_move(event: events.VoiceUserMove):
    """User moves between voice channels"""
    member = event.member
    before_channel = event.before_channel
    after_channel = event.after_channel
    print(f"{member} moved from {before_channel.name} to {after_channel.name}")

Role Events

@listen(events.RoleCreate)
async def on_role_create(event: events.RoleCreate):
    """New role created"""
    role = event.role
    guild = event.guild
    print(f"Role created in {guild.name}: {role.name}")

@listen(events.RoleUpdate)
async def on_role_update(event: events.RoleUpdate):
    """Role updated"""
    before = event.before
    after = event.after
    print(f"Role updated: {after.name}")

@listen(events.RoleDelete)
async def on_role_delete(event: events.RoleDelete):
    """Role deleted"""
    role = event.role
    guild = event.guild
    print(f"Role deleted from {guild.name}: {role.name}")

Ban Events

@listen(events.BanCreate)
async def on_member_ban(event: events.BanCreate):
    """Member banned from guild"""
    ban = event.ban
    user = ban.user
    guild = event.guild
    reason = ban.reason or "No reason provided"
    print(f"{user} was banned from {guild.name}: {reason}")

@listen(events.BanRemove)
async def on_member_unban(event: events.BanRemove):
    """Member unbanned from guild"""
    user = event.user
    guild = event.guild
    print(f"{user} was unbanned from {guild.name}")

Interaction Events

@listen(events.InteractionCreate)
async def on_interaction(event: events.InteractionCreate):
    """Any interaction received"""
    interaction = event.interaction
    print(f"Interaction received: {interaction.type}")

Internal Framework Events

Bot Lifecycle Events

@listen(events.Startup)
async def on_startup(event: events.Startup):
    """Bot starting up"""
    print("Bot is starting up...")

@listen(events.Login)
async def on_login(event: events.Login):
    """Bot logged in"""
    print("Bot has logged in to Discord")

@listen(events.Ready)
async def on_ready(event: events.Ready):
    """Bot is ready to receive events"""
    print(f"Bot is ready! Logged in as {event.user}")

@listen(events.WebsocketReady)
async def on_websocket_ready(event: events.WebsocketReady):
    """WebSocket connection established"""
    print("WebSocket connection ready")

Command Events

@listen(events.CommandCompletion)
async def on_command_complete(event: events.CommandCompletion):
    """Command completed successfully"""
    ctx = event.ctx
    command = event.command
    print(f"Command {command.name} completed by {ctx.author}")

@listen(events.CommandError)
async def on_command_error(event: events.CommandError):
    """Command encountered an error"""
    ctx = event.ctx
    error = event.error
    command = event.command
    
    print(f"Error in command {command.name}: {error}")
    
    # Send error message to user
    await ctx.send(f"An error occurred: {str(error)}", ephemeral=True)

Component Events

@listen(events.Component)
async def on_component_interaction(event: events.Component):
    """Generic component interaction"""
    ctx = event.ctx
    print(f"Component interaction: {ctx.custom_id}")

@listen(events.ComponentCompletion)
async def on_component_complete(event: events.ComponentCompletion):
    """Component interaction completed"""
    ctx = event.ctx
    print(f"Component {ctx.custom_id} completed")

@listen(events.ComponentError)
async def on_component_error(event: events.ComponentError):
    """Component interaction error"""
    ctx = event.ctx
    error = event.error
    print(f"Component error in {ctx.custom_id}: {error}")

@listen(events.ButtonPressed)
async def on_button_press(event: events.ButtonPressed):
    """Button was pressed (convenience event)"""
    ctx = event.ctx
    print(f"Button pressed: {ctx.custom_id}")

@listen(events.Select)
async def on_select_menu(event: events.Select):
    """Select menu used (convenience event)"""
    ctx = event.ctx
    values = ctx.values
    print(f"Select menu {ctx.custom_id} used with values: {values}")

Modal Events

@listen(events.ModalCompletion)
async def on_modal_complete(event: events.ModalCompletion):
    """Modal submitted successfully"""
    ctx = event.ctx
    print(f"Modal {ctx.custom_id} submitted")

@listen(events.ModalError)
async def on_modal_error(event: events.ModalError):
    """Modal submission error"""
    ctx = event.ctx
    error = event.error
    print(f"Modal error in {ctx.custom_id}: {error}")

Extension Events

@listen(events.ExtensionLoad)
async def on_extension_load(event: events.ExtensionLoad):
    """Extension loaded"""
    extension = event.extension
    print(f"Extension loaded: {extension.name}")

@listen(events.ExtensionUnload)
async def on_extension_unload(event: events.ExtensionUnload):
    """Extension unloaded"""
    extension = event.extension
    print(f"Extension unloaded: {extension.name}")

Error Events

@listen(events.Error)
async def on_general_error(event: events.Error):
    """General error occurred"""
    error = event.error
    print(f"General error: {error}")

@listen(events.AutocompleteError)
async def on_autocomplete_error(event: events.AutocompleteError):
    """Autocomplete error"""
    ctx = event.ctx
    error = event.error
    print(f"Autocomplete error: {error}")

Event Base Classes

BaseEvent

class BaseEvent:
    """Base class for all events"""
    bot: Client
    
class RawGatewayEvent(BaseEvent):
    """Raw gateway event data"""
    data: dict
    sequence: int
    event_name: str

class GuildEvent(BaseEvent):
    """Base for guild-related events"""
    guild: Guild

Advanced Event Features

Event Filtering

@listen(events.MessageCreate)
async def on_message_in_channel(event: events.MessageCreate):
    """Only process messages in specific channel"""
    message = event.message
    
    # Filter by channel
    if message.channel.id != SPECIFIC_CHANNEL_ID:
        return
        
    # Process message
    await process_message(message)

@listen(events.MemberAdd)
async def on_member_join_specific_guild(event: events.MemberAdd):
    """Only process joins in specific guild"""
    member = event.member
    
    if member.guild.id != SPECIFIC_GUILD_ID:
        return
        
    await welcome_member(member)

Conditional Event Handling

@listen(events.MessageCreate)
async def on_message_with_conditions(event: events.MessageCreate):
    """Message event with multiple conditions"""
    message = event.message
    
    # Skip bot messages
    if message.author.bot:
        return
        
    # Only process messages with attachments
    if not message.attachments:
        return
        
    # Only process in guild channels
    if not message.guild:
        return
        
    # Process the message
    await handle_attachment_message(message)

Event Data Access

@listen(events.MessageUpdate)
async def on_message_edit_detailed(event: events.MessageUpdate):
    """Access detailed message edit data"""
    before = event.before  # Message before edit (may be None if not cached)
    after = event.after    # Message after edit
    
    if before and after:
        # Compare content changes
        if before.content != after.content:
            print(f"Content changed from: {before.content}")
            print(f"Content changed to: {after.content}")
            
        # Check embed changes
        if before.embeds != after.embeds:
            print("Embeds were modified")

Event Listener Patterns

Class Method Listeners

class MyBot(Client):
    def __init__(self):
        super().__init__(token="TOKEN")
    
    @listen()
    async def on_ready(self, event: events.Ready):
        """Method-based event listener"""
        print(f"{self.user} is ready!")
        
    @listen(events.MessageCreate)  
    async def handle_messages(self, event: events.MessageCreate):
        """Handle messages in class context"""
        message = event.message
        if message.content.startswith(f"<@{self.user.id}>"):
            await message.reply("You mentioned me!")

Decorator Stacking

@listen(events.GuildJoin)
@listen(events.GuildAvailable)
async def on_guild_accessible(event):
    """Listen to multiple related events"""
    guild = event.guild
    print(f"Guild accessible: {guild.name}")

Install with Tessl CLI

npx tessl i tessl/pypi-discord-py-interactions

docs

client.md

commands.md

components.md

discord-models.md

events.md

extensions.md

index.md

tile.json