CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-nextcord

A Python wrapper for the Discord API forked from discord.py

Pending
Overview
Eval results
Files

events.mddocs/

Nextcord Events

Event handling system for Discord gateway events and raw event data for advanced use cases with comprehensive event monitoring capabilities.

Event System

Core event handling mechanism for responding to Discord gateway events.

Event Decorator and Registration { .api }

import nextcord
from nextcord.ext import commands
from typing import Any, Callable, Optional, Dict, List
from datetime import datetime

# Event registration using decorator
@bot.event
async def on_ready():
    """Called when the client has successfully connected to Discord.
    
    This event is called when the bot has finished logging in and setting up.
    It's only called once per login session.
    """
    print(f'{bot.user} has connected to Discord!')
    print(f'Bot is in {len(bot.guilds)} guilds')
    print(f'Connected at: {datetime.now()}')
    
    # Set bot status
    activity = nextcord.Activity(
        type=nextcord.ActivityType.watching,
        name=f"{len(bot.guilds)} servers"
    )
    await bot.change_presence(status=nextcord.Status.online, activity=activity)

# Manual event registration
async def my_event_handler():
    """Custom event handler function."""
    print("Custom event triggered!")

# Register event manually
bot.add_listener(my_event_handler, 'on_ready')

# Multiple listeners for the same event
@bot.event
async def on_ready():
    """First ready handler."""
    print("First ready handler called")

@bot.listen('on_ready')  # Use listen() to add additional handlers
async def second_ready_handler():
    """Second ready handler."""
    print("Second ready handler called")

# Event with parameters
@bot.event
async def on_message(message: nextcord.Message):
    """Called when a message is sent in a channel the bot can see.
    
    Parameters
    ----------
    message: nextcord.Message
        The message that was sent.
    """
    # Ignore messages from bots
    if message.author.bot:
        return
    
    print(f'{message.author}: {message.content}')
    
    # Process commands (required for command framework)
    await bot.process_commands(message)

# Event error handling
@bot.event
async def on_error(event: str, *args, **kwargs):
    """Called when an event raises an uncaught exception.
    
    Parameters
    ----------
    event: str
        The name of the event that raised the exception.
    *args
        The positional arguments passed to the event.
    **kwargs
        The keyword arguments passed to the event.
    """
    import traceback
    
    print(f'An error occurred in {event}:')
    traceback.print_exc()
    
    # Log error to file or database
    with open('error_log.txt', 'a') as f:
        f.write(f'{datetime.now()}: Error in {event}\n')
        traceback.print_exc(file=f)
        f.write('\n')

Connection Events

Events related to bot connection and disconnection states.

Connection State Events { .api }

@bot.event
async def on_connect():
    """Called when the client has successfully connected to Discord.
    
    This is different from on_ready in that it's called every time
    the websocket connection is established (including reconnections).
    """
    print(f'Connected to Discord at {datetime.now()}')

@bot.event
async def on_disconnect():
    """Called when the client has disconnected from Discord.
    
    This could be due to internet issues, Discord issues, or
    the bot being shut down.
    """
    print(f'Disconnected from Discord at {datetime.now()}')

@bot.event
async def on_resumed():
    """Called when the client has resumed a session.
    
    This is called when the websocket connection has been resumed
    after a disconnect, rather than creating a new session.
    """
    print(f'Resumed Discord connection at {datetime.now()}')

# Advanced connection monitoring
class ConnectionMonitor:
    """Monitor connection events and track statistics."""
    
    def __init__(self, bot):
        self.bot = bot
        self.connection_count = 0
        self.disconnect_count = 0
        self.resume_count = 0
        self.first_connect_time = None
        self.last_disconnect_time = None
        self.uptime_start = None
    
    @commands.Cog.listener()
    async def on_connect(self):
        """Track connection events."""
        self.connection_count += 1
        current_time = datetime.now()
        
        if self.first_connect_time is None:
            self.first_connect_time = current_time
            self.uptime_start = current_time
        
        print(f'Connection #{self.connection_count} established')
    
    @commands.Cog.listener()
    async def on_disconnect(self):
        """Track disconnection events."""
        self.disconnect_count += 1
        self.last_disconnect_time = datetime.now()
        
        print(f'Disconnect #{self.disconnect_count} occurred')
    
    @commands.Cog.listener()
    async def on_resumed(self):
        """Track resume events."""
        self.resume_count += 1
        print(f'Resume #{self.resume_count} occurred')
    
    @commands.Cog.listener()
    async def on_ready(self):
        """Log ready state with connection info."""
        current_time = datetime.now()
        
        if self.uptime_start:
            uptime = current_time - self.uptime_start
            print(f'Bot ready! Uptime: {uptime}')
        
        print(f'Connection stats: {self.connection_count} connects, '
              f'{self.disconnect_count} disconnects, {self.resume_count} resumes')

# Add the connection monitor
bot.add_cog(ConnectionMonitor(bot))

# Sharded connection events (for AutoShardedBot)
@bot.event
async def on_shard_connect(shard_id: int):
    """Called when a shard connects.
    
    Parameters
    ----------
    shard_id: int
        The ID of the shard that connected.
    """
    print(f'Shard {shard_id} connected')

@bot.event
async def on_shard_disconnect(shard_id: int):
    """Called when a shard disconnects.
    
    Parameters
    ----------
    shard_id: int
        The ID of the shard that disconnected.
    """
    print(f'Shard {shard_id} disconnected')

@bot.event
async def on_shard_ready(shard_id: int):
    """Called when a shard becomes ready.
    
    Parameters
    ----------
    shard_id: int
        The ID of the shard that became ready.
    """
    print(f'Shard {shard_id} is ready')

@bot.event
async def on_shard_resumed(shard_id: int):
    """Called when a shard resumes.
    
    Parameters
    ----------
    shard_id: int
        The ID of the shard that resumed.
    """
    print(f'Shard {shard_id} resumed')

Message Events

Events related to message creation, editing, and deletion.

Message Lifecycle Events { .api }

@bot.event
async def on_message(message: nextcord.Message):
    """Called when a message is created.
    
    Parameters
    ----------
    message: nextcord.Message
        The message that was created.
    """
    # Skip bot messages
    if message.author.bot:
        return
    
    # Log message statistics
    print(f'Message from {message.author} in {message.channel}: {len(message.content)} chars')
    
    # Auto-moderation example
    if any(word in message.content.lower() for word in ['spam', 'advertisement']):
        await message.delete()
        await message.channel.send(
            f"{message.author.mention}, that message was removed for spam.",
            delete_after=5
        )
        return
    
    # Process commands
    await bot.process_commands(message)

@bot.event
async def on_message_edit(before: nextcord.Message, after: nextcord.Message):
    """Called when a message is edited.
    
    Parameters
    ----------
    before: nextcord.Message
        The message before editing.
    after: nextcord.Message
        The message after editing.
    """
    # Skip bot messages
    if before.author.bot:
        return
    
    # Skip if content didn't change (embed updates, etc.)
    if before.content == after.content:
        return
    
    # Log edit to moderation channel
    mod_channel = nextcord.utils.get(after.guild.channels, name='mod-logs')
    if mod_channel:
        embed = nextcord.Embed(
            title="📝 Message Edited",
            color=nextcord.Color.orange(),
            timestamp=datetime.now()
        )
        
        embed.add_field(name="Author", value=after.author.mention, inline=True)
        embed.add_field(name="Channel", value=after.channel.mention, inline=True)
        embed.add_field(name="Message ID", value=after.id, inline=True)
        
        # Show before/after content (truncated)
        before_content = before.content[:1000] + "..." if len(before.content) > 1000 else before.content
        after_content = after.content[:1000] + "..." if len(after.content) > 1000 else after.content
        
        embed.add_field(name="Before", value=before_content or "*No content*", inline=False)
        embed.add_field(name="After", value=after_content or "*No content*", 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: nextcord.Message):
    """Called when a message is deleted.
    
    Parameters
    ----------
    message: nextcord.Message
        The message that was deleted.
    """
    # Skip bot messages
    if message.author.bot:
        return
    
    # Log deletion
    mod_channel = nextcord.utils.get(message.guild.channels, name='mod-logs')
    if mod_channel:
        embed = nextcord.Embed(
            title="🗑️ Message Deleted",
            color=nextcord.Color.red(),
            timestamp=datetime.now()
        )
        
        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)
        
        # Show deleted content (truncated)
        content = message.content[:1500] + "..." if len(message.content) > 1500 else message.content
        embed.add_field(name="Content", value=content or "*No text content*", inline=False)
        
        # Show attachments if any
        if message.attachments:
            attachment_info = []
            for attachment in message.attachments[:5]:  # Show first 5
                attachment_info.append(f"• {attachment.filename} ({attachment.size} bytes)")
            
            embed.add_field(
                name=f"Attachments ({len(message.attachments)})",
                value="\n".join(attachment_info),
                inline=False
            )
        
        embed.set_footer(text=f"Originally sent", icon_url=message.author.display_avatar.url)
        embed.timestamp = message.created_at
        
        await mod_channel.send(embed=embed)

# Bulk message operations
@bot.event
async def on_bulk_message_delete(messages: List[nextcord.Message]):
    """Called when messages are bulk deleted.
    
    Parameters
    ----------
    messages: List[nextcord.Message]
        The messages that were deleted.
    """
    if not messages:
        return
    
    # Get guild and channel from first message
    first_message = messages[0]
    guild = first_message.guild
    channel = first_message.channel
    
    # Log bulk deletion
    mod_channel = nextcord.utils.get(guild.channels, name='mod-logs')
    if mod_channel:
        embed = nextcord.Embed(
            title="🗑️ Bulk Message Deletion",
            description=f"{len(messages)} messages deleted from {channel.mention}",
            color=nextcord.Color.red(),
            timestamp=datetime.now()
        )
        
        # Show message count by author
        author_counts = {}
        for message in messages:
            author_counts[message.author] = author_counts.get(message.author, 0) + 1
        
        # Top 5 authors
        top_authors = sorted(author_counts.items(), key=lambda x: x[1], reverse=True)[:5]
        author_text = []
        for author, count in top_authors:
            author_text.append(f"• {author.mention}: {count} messages")
        
        embed.add_field(name="Messages by Author", value="\n".join(author_text), inline=False)
        
        # Time range
        if len(messages) > 1:
            oldest = min(messages, key=lambda m: m.created_at)
            newest = max(messages, key=lambda m: m.created_at)
            
            embed.add_field(
                name="Time Range",
                value=f"From {oldest.created_at.strftime('%Y-%m-%d %H:%M')} to {newest.created_at.strftime('%Y-%m-%d %H:%M')}",
                inline=False
            )
        
        await mod_channel.send(embed=embed)

# Message reaction events
@bot.event
async def on_reaction_add(reaction: nextcord.Reaction, user: nextcord.User):
    """Called when a reaction is added to a message.
    
    Parameters
    ----------
    reaction: nextcord.Reaction
        The reaction that was added.
    user: nextcord.User
        The user who added the reaction.
    """
    # Skip bot reactions
    if user.bot:
        return
    
    message = reaction.message
    
    # Role reaction system example
    if message.id == ROLE_MESSAGE_ID:  # Configure this
        role_mapping = {
            '🎮': 'Gamer',
            '🎵': 'Music Lover',
            '📚': 'Bookworm',
            '🎨': 'Artist'
        }
        
        role_name = role_mapping.get(str(reaction.emoji))
        if role_name:
            role = nextcord.utils.get(message.guild.roles, name=role_name)
            if role:
                member = message.guild.get_member(user.id)
                if member:
                    await member.add_roles(role, reason="Role reaction")
                    print(f"Added {role.name} to {member}")

@bot.event
async def on_reaction_remove(reaction: nextcord.Reaction, user: nextcord.User):
    """Called when a reaction is removed from a message.
    
    Parameters
    ----------
    reaction: nextcord.Reaction
        The reaction that was removed.
    user: nextcord.User
        The user who removed the reaction.
    """
    # Skip bot reactions
    if user.bot:
        return
    
    message = reaction.message
    
    # Remove role when reaction is removed
    if message.id == ROLE_MESSAGE_ID:  # Configure this
        role_mapping = {
            '🎮': 'Gamer',
            '🎵': 'Music Lover', 
            '📚': 'Bookworm',
            '🎨': 'Artist'
        }
        
        role_name = role_mapping.get(str(reaction.emoji))
        if role_name:
            role = nextcord.utils.get(message.guild.roles, name=role_name)
            if role:
                member = message.guild.get_member(user.id)
                if member:
                    await member.remove_roles(role, reason="Role reaction removed")
                    print(f"Removed {role.name} from {member}")

@bot.event
async def on_reaction_clear(message: nextcord.Message, reactions: List[nextcord.Reaction]):
    """Called when all reactions are cleared from a message.
    
    Parameters
    ----------
    message: nextcord.Message
        The message that had reactions cleared.
    reactions: List[nextcord.Reaction]
        The reactions that were cleared.
    """
    print(f"All reactions cleared from message {message.id} in {message.channel}")

Member and Guild Events

Events related to guild membership and server changes.

Member Events { .api }

@bot.event
async def on_member_join(member: nextcord.Member):
    """Called when a member joins a guild.
    
    Parameters
    ----------
    member: nextcord.Member
        The member who joined.
    """
    guild = member.guild
    
    # Send welcome message
    welcome_channel = nextcord.utils.get(guild.channels, name='welcome')
    if welcome_channel:
        embed = nextcord.Embed(
            title="👋 Welcome!",
            description=f"Welcome to **{guild.name}**, {member.mention}!",
            color=nextcord.Color.green(),
            timestamp=datetime.now()
        )
        
        embed.add_field(name="Member Count", value=f"You are member #{guild.member_count}", inline=True)
        embed.add_field(name="Account Created", value=member.created_at.strftime("%B %d, %Y"), inline=True)
        
        embed.set_thumbnail(url=member.display_avatar.url)
        embed.set_footer(text=f"ID: {member.id}")
        
        await welcome_channel.send(embed=embed)
    
    # Auto-role assignment
    auto_role = nextcord.utils.get(guild.roles, name='Member')
    if auto_role:
        try:
            await member.add_roles(auto_role, reason="Auto-role on join")
            print(f"Gave {auto_role.name} to {member}")
        except nextcord.Forbidden:
            print(f"Failed to give auto-role to {member} - insufficient permissions")
    
    # Log join
    log_channel = nextcord.utils.get(guild.channels, name='member-logs')
    if log_channel:
        embed = nextcord.Embed(
            title="📈 Member Joined",
            color=nextcord.Color.green(),
            timestamp=datetime.now()
        )
        
        embed.add_field(name="Member", value=f"{member} ({member.mention})", inline=True)
        embed.add_field(name="ID", value=member.id, inline=True)
        embed.add_field(name="Total Members", value=guild.member_count, inline=True)
        
        # Account age
        account_age = datetime.now() - member.created_at
        embed.add_field(name="Account Age", value=f"{account_age.days} days", inline=True)
        
        embed.set_thumbnail(url=member.display_avatar.url)
        
        await log_channel.send(embed=embed)

@bot.event
async def on_member_remove(member: nextcord.Member):
    """Called when a member leaves a guild.
    
    Parameters
    ----------
    member: nextcord.Member
        The member who left.
    """
    guild = member.guild
    
    # Send farewell message
    farewell_channel = nextcord.utils.get(guild.channels, name='farewell')
    if farewell_channel:
        embed = nextcord.Embed(
            title="👋 Goodbye!",
            description=f"**{member.display_name}** has left the server.",
            color=nextcord.Color.red(),
            timestamp=datetime.now()
        )
        
        embed.add_field(name="Member Count", value=f"We now have {guild.member_count} members", inline=True)
        
        # Calculate how long they were in the server
        if member.joined_at:
            time_in_server = datetime.now() - member.joined_at.replace(tzinfo=None)
            embed.add_field(name="Time in Server", value=f"{time_in_server.days} days", inline=True)
        
        embed.set_thumbnail(url=member.display_avatar.url)
        
        await farewell_channel.send(embed=embed)
    
    # Log leave
    log_channel = nextcord.utils.get(guild.channels, name='member-logs')
    if log_channel:
        embed = nextcord.Embed(
            title="📉 Member Left",
            color=nextcord.Color.red(),
            timestamp=datetime.now()
        )
        
        embed.add_field(name="Member", value=f"{member} ({member.mention})", inline=True)
        embed.add_field(name="ID", value=member.id, inline=True)
        embed.add_field(name="Total Members", value=guild.member_count, inline=True)
        
        # Show roles they had
        if member.roles[1:]:  # Skip @everyone
            roles = [role.name for role in member.roles[1:][:10]]  # First 10 roles
            embed.add_field(name="Roles", value=", ".join(roles), inline=False)
        
        embed.set_thumbnail(url=member.display_avatar.url)
        
        await log_channel.send(embed=embed)

@bot.event
async def on_member_update(before: nextcord.Member, after: nextcord.Member):
    """Called when a member updates their profile or guild-specific settings.
    
    Parameters
    ----------
    before: nextcord.Member
        The member before the update.
    after: nextcord.Member
        The member after the update.
    """
    # Skip if no relevant changes
    if (before.display_name == after.display_name and 
        before.roles == after.roles and
        before.status == after.status):
        return
    
    log_channel = nextcord.utils.get(after.guild.channels, name='member-logs')
    if not log_channel:
        return
    
    embed = nextcord.Embed(
        title="👤 Member Updated",
        color=nextcord.Color.blue(),
        timestamp=datetime.now()
    )
    
    embed.add_field(name="Member", value=after.mention, inline=True)
    embed.add_field(name="ID", value=after.id, inline=True)
    
    # Check for nickname changes
    if before.display_name != after.display_name:
        embed.add_field(
            name="Nickname Change",
            value=f"**Before:** {before.display_name}\n**After:** {after.display_name}",
            inline=False
        )
    
    # Check for role changes
    if before.roles != after.roles:
        added_roles = [role for role in after.roles if role not in before.roles]
        removed_roles = [role for role in before.roles if role not in after.roles]
        
        if added_roles:
            embed.add_field(
                name="Roles Added",
                value=", ".join([role.mention for role in added_roles]),
                inline=False
            )
        
        if removed_roles:
            embed.add_field(
                name="Roles Removed", 
                value=", ".join([role.mention for role in removed_roles]),
                inline=False
            )
    
    # Only send if there are actual changes to log
    if len(embed.fields) > 2:  # More than just member and ID fields
        embed.set_thumbnail(url=after.display_avatar.url)
        await log_channel.send(embed=embed)

# Guild events
@bot.event
async def on_guild_join(guild: nextcord.Guild):
    """Called when the bot joins a guild.
    
    Parameters
    ----------
    guild: nextcord.Guild
        The guild that was joined.
    """
    print(f"Joined guild: {guild.name} (ID: {guild.id})")
    print(f"Guild has {guild.member_count} members")
    
    # Send a greeting message to the system channel
    if guild.system_channel:
        embed = nextcord.Embed(
            title="👋 Hello!",
            description="Thank you for adding me to your server!",
            color=nextcord.Color.green()
        )
        
        embed.add_field(
            name="Getting Started",
            value="Use `/help` to see available commands.",
            inline=False
        )
        
        embed.add_field(
            name="Support",
            value="Need help? Join our support server!",
            inline=False
        )
        
        try:
            await guild.system_channel.send(embed=embed)
        except nextcord.Forbidden:
            print(f"Cannot send greeting to {guild.name} - no permission")

@bot.event
async def on_guild_remove(guild: nextcord.Guild):
    """Called when the bot leaves a guild.
    
    Parameters
    ----------
    guild: nextcord.Guild
        The guild that was left.
    """
    print(f"Left guild: {guild.name} (ID: {guild.id})")
    
    # Clean up any guild-specific data
    # This is where you'd remove database entries, etc.
    await cleanup_guild_data(guild.id)

@bot.event
async def on_guild_update(before: nextcord.Guild, after: nextcord.Guild):
    """Called when a guild updates.
    
    Parameters
    ----------
    before: nextcord.Guild
        The guild before the update.
    after: nextcord.Guild
        The guild after the update.
    """
    changes = []
    
    if before.name != after.name:
        changes.append(f"Name: {before.name} → {after.name}")
    
    if before.description != after.description:
        changes.append(f"Description changed")
    
    if before.icon != after.icon:
        changes.append(f"Icon changed")
    
    if before.banner != after.banner:
        changes.append(f"Banner changed")
    
    if before.verification_level != after.verification_level:
        changes.append(f"Verification level: {before.verification_level} → {after.verification_level}")
    
    if changes:
        log_channel = nextcord.utils.get(after.channels, name='server-logs')
        if log_channel:
            embed = nextcord.Embed(
                title="🏠 Server Updated",
                description="\n".join(changes),
                color=nextcord.Color.blue(),
                timestamp=datetime.now()
            )
            
            await log_channel.send(embed=embed)

Raw Events

Raw event data for advanced use cases requiring detailed event information.

Raw Event Handling { .api }

@bot.event
async def on_raw_message_delete(payload: nextcord.RawMessageDeleteEvent):
    """Called when a message is deleted.
    
    This is called even if the message is not in the bot's cache.
    
    Parameters
    ----------
    payload: nextcord.RawMessageDeleteEvent
        The raw event payload.
    """
    # Get the channel
    channel = bot.get_channel(payload.channel_id)
    if not channel:
        return
    
    # Check if we have the cached message
    if payload.cached_message:
        message = payload.cached_message
        print(f"Cached message deleted: {message.content[:50]}...")
    else:
        print(f"Uncached message deleted in {channel.name} (ID: {payload.message_id})")
    
    # Log to database or external service
    await log_message_deletion({
        'message_id': payload.message_id,
        'channel_id': payload.channel_id,
        'guild_id': payload.guild_id,
        'cached': payload.cached_message is not None,
        'timestamp': datetime.now()
    })

@bot.event
async def on_raw_message_edit(payload: nextcord.RawMessageUpdateEvent):
    """Called when a message is edited.
    
    This is called even if the message is not in the bot's cache.
    
    Parameters
    ----------
    payload: nextcord.RawMessageUpdateEvent
        The raw event payload.
    """
    # Skip if partial data (e.g., embed updates)
    if 'content' not in payload.data:
        return
    
    channel = bot.get_channel(payload.channel_id)
    if not channel:
        return
    
    message_id = payload.message_id
    new_content = payload.data['content']
    
    # Check if we have cached versions
    if payload.cached_message:
        old_content = payload.cached_message.content
        author = payload.cached_message.author
        
        print(f"Message edit by {author}: {old_content[:50]}... → {new_content[:50]}...")
    else:
        print(f"Uncached message edited in {channel.name} (ID: {message_id})")
    
    # Log edit to database
    await log_message_edit({
        'message_id': message_id,
        'channel_id': payload.channel_id,
        'guild_id': payload.guild_id,
        'new_content': new_content,
        'cached': payload.cached_message is not None,
        'timestamp': datetime.now()
    })

@bot.event
async def on_raw_reaction_add(payload: nextcord.RawReactionActionEvent):
    """Called when a reaction is added to a message.
    
    This is called even if the message is not in the bot's cache.
    
    Parameters
    ----------
    payload: nextcord.RawReactionActionEvent
        The raw event payload.
    """
    # Skip bot reactions
    if payload.user_id == bot.user.id:
        return
    
    # Get the channel and guild
    channel = bot.get_channel(payload.channel_id)
    guild = bot.get_guild(payload.guild_id) if payload.guild_id else None
    
    if not channel:
        return
    
    # Get user and emoji info
    user = bot.get_user(payload.user_id)
    emoji = payload.emoji
    
    print(f"Reaction {emoji} added by {user} to message {payload.message_id}")
    
    # Handle starboard functionality
    if str(emoji) == '⭐':
        await handle_starboard_reaction(payload, added=True)
    
    # Handle role reactions for uncached messages
    await handle_role_reactions(payload, added=True)

@bot.event
async def on_raw_reaction_remove(payload: nextcord.RawReactionActionEvent):
    """Called when a reaction is removed from a message.
    
    Parameters
    ----------
    payload: nextcord.RawReactionActionEvent
        The raw event payload.
    """
    # Skip bot reactions
    if payload.user_id == bot.user.id:
        return
    
    user = bot.get_user(payload.user_id)
    emoji = payload.emoji
    
    print(f"Reaction {emoji} removed by {user} from message {payload.message_id}")
    
    # Handle starboard functionality
    if str(emoji) == '⭐':
        await handle_starboard_reaction(payload, added=False)
    
    # Handle role reactions
    await handle_role_reactions(payload, added=False)

@bot.event
async def on_raw_reaction_clear(payload: nextcord.RawReactionClearEvent):
    """Called when all reactions are cleared from a message.
    
    Parameters
    ----------
    payload: nextcord.RawReactionClearEvent
        The raw event payload.
    """
    print(f"All reactions cleared from message {payload.message_id}")
    
    # Remove from starboard if applicable
    await remove_from_starboard(payload.message_id)

# Advanced raw event handlers
async def handle_starboard_reaction(payload: nextcord.RawReactionActionEvent, added: bool):
    """Handle starboard functionality for raw reactions."""
    
    # Get the message (try cache first, then fetch)
    channel = bot.get_channel(payload.channel_id)
    if not channel:
        return
    
    try:
        message = await channel.fetch_message(payload.message_id)
    except nextcord.NotFound:
        return
    
    # Skip bot messages
    if message.author.bot:
        return
    
    # Count star reactions
    star_count = 0
    for reaction in message.reactions:
        if str(reaction.emoji) == '⭐':
            star_count = reaction.count
            break
    
    # Starboard threshold
    if star_count >= 3:
        await add_to_starboard(message, star_count)
    else:
        await remove_from_starboard(message.id)

async def handle_role_reactions(payload: nextcord.RawReactionActionEvent, added: bool):
    """Handle role reactions for raw events."""
    
    # Check if this is a role reaction message
    if payload.message_id != ROLE_MESSAGE_ID:  # Configure this
        return
    
    guild = bot.get_guild(payload.guild_id)
    if not guild:
        return
    
    member = guild.get_member(payload.user_id)
    if not member or member.bot:
        return
    
    # Role mapping
    role_mapping = {
        '🎮': 'Gamer',
        '🎵': 'Music Lover',
        '📚': 'Bookworm',
        '🎨': 'Artist'
    }
    
    role_name = role_mapping.get(str(payload.emoji))
    if not role_name:
        return
    
    role = nextcord.utils.get(guild.roles, name=role_name)
    if not role:
        return
    
    try:
        if added:
            await member.add_roles(role, reason="Role reaction")
            print(f"Added {role.name} to {member} via raw reaction")
        else:
            await member.remove_roles(role, reason="Role reaction removed")
            print(f"Removed {role.name} from {member} via raw reaction")
    except nextcord.Forbidden:
        print(f"Cannot modify roles for {member}")

# Database logging functions (implement according to your database)
async def log_message_deletion(data: dict):
    """Log message deletion to database."""
    # Implement database logging
    pass

async def log_message_edit(data: dict):
    """Log message edit to database."""
    # Implement database logging
    pass

async def add_to_starboard(message: nextcord.Message, star_count: int):
    """Add message to starboard."""
    # Implement starboard functionality
    pass

async def remove_from_starboard(message_id: int):
    """Remove message from starboard."""
    # Implement starboard functionality
    pass

async def cleanup_guild_data(guild_id: int):
    """Clean up guild-specific data when bot leaves."""
    # Implement cleanup logic
    pass

Event Monitoring and Analytics

Advanced event monitoring for bot analytics and performance tracking.

Event Statistics and Monitoring { .api }

from collections import defaultdict, deque
import time

class EventMonitor:
    """Monitor and track bot events for analytics."""
    
    def __init__(self, bot):
        self.bot = bot
        self.event_counts = defaultdict(int)
        self.event_times = defaultdict(lambda: deque(maxlen=1000))  # Last 1000 events
        self.guild_stats = defaultdict(lambda: defaultdict(int))
        self.user_activity = defaultdict(lambda: defaultdict(int))
        self.start_time = time.time()
    
    def track_event(self, event_name: str, guild_id: Optional[int] = None, user_id: Optional[int] = None):
        """Track an event occurrence."""
        current_time = time.time()
        
        # Global event tracking
        self.event_counts[event_name] += 1
        self.event_times[event_name].append(current_time)
        
        # Guild-specific tracking
        if guild_id:
            self.guild_stats[guild_id][event_name] += 1
        
        # User-specific tracking
        if user_id:
            self.user_activity[user_id][event_name] += 1
    
    def get_event_rate(self, event_name: str, window_seconds: int = 3600) -> float:
        """Get event rate per second over the specified window."""
        current_time = time.time()
        cutoff_time = current_time - window_seconds
        
        recent_events = [t for t in self.event_times[event_name] if t > cutoff_time]
        return len(recent_events) / window_seconds if recent_events else 0.0
    
    def get_statistics(self) -> Dict[str, Any]:
        """Get comprehensive event statistics."""
        uptime = time.time() - self.start_time
        
        stats = {
            'uptime_seconds': uptime,
            'uptime_formatted': f"{uptime // 3600:.0f}h {(uptime % 3600) // 60:.0f}m",
            'total_events': sum(self.event_counts.values()),
            'event_counts': dict(self.event_counts),
            'event_rates_per_hour': {},
            'top_guilds': {},
            'top_users': {}
        }
        
        # Calculate event rates
        for event_name in self.event_counts:
            rate = self.get_event_rate(event_name, 3600)  # Last hour
            stats['event_rates_per_hour'][event_name] = round(rate * 3600, 2)
        
        # Top guilds by activity
        guild_totals = {guild_id: sum(events.values()) for guild_id, events in self.guild_stats.items()}
        top_guilds = sorted(guild_totals.items(), key=lambda x: x[1], reverse=True)[:10]
        
        for guild_id, total in top_guilds:
            guild = self.bot.get_guild(guild_id)
            guild_name = guild.name if guild else f"Unknown ({guild_id})"
            stats['top_guilds'][guild_name] = total
        
        # Top users by activity
        user_totals = {user_id: sum(events.values()) for user_id, events in self.user_activity.items()}
        top_users = sorted(user_totals.items(), key=lambda x: x[1], reverse=True)[:10]
        
        for user_id, total in top_users:
            user = self.bot.get_user(user_id)
            user_name = str(user) if user else f"Unknown ({user_id})"
            stats['top_users'][user_name] = total
        
        return stats

# Initialize event monitor
event_monitor = EventMonitor(bot)

# Enhanced event handlers with monitoring
@bot.event
async def on_message(message: nextcord.Message):
    """Enhanced message handler with monitoring."""
    event_monitor.track_event('message', message.guild.id if message.guild else None, message.author.id)
    
    # Skip bot messages
    if message.author.bot:
        return
    
    # Track message characteristics
    if len(message.content) > 100:
        event_monitor.track_event('long_message', message.guild.id if message.guild else None)
    
    if message.attachments:
        event_monitor.track_event('message_with_attachment', message.guild.id if message.guild else None)
    
    if message.mentions:
        event_monitor.track_event('message_with_mentions', message.guild.id if message.guild else None)
    
    # Process commands
    await bot.process_commands(message)

@bot.event
async def on_member_join(member: nextcord.Member):
    """Enhanced member join handler with monitoring."""
    event_monitor.track_event('member_join', member.guild.id, member.id)
    
    # Track account age
    account_age_days = (datetime.now() - member.created_at.replace(tzinfo=None)).days
    
    if account_age_days < 7:
        event_monitor.track_event('new_account_join', member.guild.id)
    elif account_age_days < 30:
        event_monitor.track_event('young_account_join', member.guild.id)
    
    # Original join logic here...

@bot.event
async def on_command(ctx: commands.Context):
    """Track command usage."""
    event_monitor.track_event('command_used', ctx.guild.id if ctx.guild else None, ctx.author.id)
    event_monitor.track_event(f'command_{ctx.command.name}', ctx.guild.id if ctx.guild else None, ctx.author.id)

@bot.event
async def on_command_error(ctx: commands.Context, error):
    """Track command errors."""
    event_monitor.track_event('command_error', ctx.guild.id if ctx.guild else None, ctx.author.id)
    event_monitor.track_event(f'error_{type(error).__name__}', ctx.guild.id if ctx.guild else None)

# Statistics command
@bot.command()
@commands.is_owner()
async def event_stats(ctx):
    """Show bot event statistics."""
    stats = event_monitor.get_statistics()
    
    embed = nextcord.Embed(
        title="📊 Bot Event Statistics",
        color=nextcord.Color.blue(),
        timestamp=datetime.now()
    )
    
    # Overview
    embed.add_field(
        name="📈 Overview",
        value=f"**Uptime:** {stats['uptime_formatted']}\n"
              f"**Total Events:** {stats['total_events']:,}",
        inline=False
    )
    
    # Top events
    top_events = sorted(stats['event_counts'].items(), key=lambda x: x[1], reverse=True)[:5]
    event_text = []
    for event, count in top_events:
        rate = stats['event_rates_per_hour'].get(event, 0)
        event_text.append(f"**{event}:** {count:,} ({rate}/hr)")
    
    embed.add_field(
        name="🔥 Top Events",
        value="\n".join(event_text),
        inline=True
    )
    
    # Top guilds
    if stats['top_guilds']:
        guild_text = []
        for guild_name, total in list(stats['top_guilds'].items())[:5]:
            guild_text.append(f"**{guild_name[:20]}:** {total:,}")
        
        embed.add_field(
            name="🏆 Top Servers",
            value="\n".join(guild_text),
            inline=True
        )
    
    await ctx.send(embed=embed)

# Performance monitoring
class PerformanceMonitor:
    """Monitor event processing performance."""
    
    def __init__(self):
        self.processing_times = defaultdict(list)
        self.slow_events = deque(maxlen=100)  # Last 100 slow events
    
    def time_event(self, event_name: str):
        """Context manager to time event processing."""
        return EventTimer(self, event_name)

class EventTimer:
    """Timer context manager for events."""
    
    def __init__(self, monitor: PerformanceMonitor, event_name: str):
        self.monitor = monitor
        self.event_name = event_name
        self.start_time = None
    
    def __enter__(self):
        self.start_time = time.perf_counter()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        duration = time.perf_counter() - self.start_time
        self.monitor.processing_times[self.event_name].append(duration)
        
        # Track slow events (> 100ms)
        if duration > 0.1:
            self.monitor.slow_events.append({
                'event': self.event_name,
                'duration': duration,
                'timestamp': time.time(),
                'had_error': exc_type is not None
            })

performance_monitor = PerformanceMonitor()

# Example of timed event handler
@bot.event
async def on_message_with_timing(message: nextcord.Message):
    """Message handler with performance monitoring."""
    with performance_monitor.time_event('on_message'):
        # Original message handling logic
        await on_message(message)

This comprehensive documentation covers all aspects of nextcord's event system, providing developers with robust tools for handling Discord events and building responsive bot applications.

Install with Tessl CLI

npx tessl i tessl/pypi-nextcord

docs

application-commands.md

channels.md

client.md

commands.md

errors.md

events.md

guild.md

index.md

messages.md

permissions.md

tasks.md

ui.md

users.md

utilities.md

voice.md

webhooks.md

tile.json