Skip to content

Core Examples


This section details examples using ScurryPy without scurrypy.ext.

Important

These examples assume you have ScurryPy set up.

If you've missed this part, it is recommended to start here.

Basic Event


Demonstrates responding to the READY event.

# --- Core library imports ---
from scurrypy import Client
from scurrypy.events import ReadyEvent

# --- Setup bot ---
client = Client(token=TOKEN)

async def on_ready(event: ReadyEvent):
    print(f"{event.user.username} is online!")

client.add_event_listener("READY", on_ready)

# --- Run the bot ---
client.run()

Basic Prefix Command


Demonstrates registering and responding to a prefix command.

Legacy

While prefix commands are supported, they are deemed a legacy feature.

Discord encourages using interactions. See the next example.

# --- Core library imports ---
from scurrypy import Client, Intents
from scurrypy.events import EventType, MessageCreateEvent

client = Client(token=TOKEN, intents=Intents.DEFAULT | Intents.MESSAGE_CONTENT)

# --- Setup bot ---
async def on_ping(event: MessageCreateEvent):
    if not event.content:
        return

    if not event.content.startswith('!ping'):
        return

    await client.channel(event.channel_id).send("Pong!")

client.add_event_listener(EventType.MESSAGE_CREATE, on_ping)

# --- Run the bot ---
client.run()

Basic Slash Command


Demonstrates registering and responding to a slash command interaction.

# --- Core library imports ---
from scurrypy import Client
from scurrypy.api.commands import SlashCommandPart
from scurrypy.api.interactions import ApplicationCommandDataModel
from scurrypy.events import EventType, InteractionEvent

# --- Setup bot ---
client = Client(token=TOKEN)

async def register_commands():
    """Register slash commands on startup (before READY)."""
    await client.guild_command(APP_ID, GUILD_ID).create(
        SlashCommandPart("greet", "Greet the bot!")
    )

client.add_startup_hook(register_commands)

async def on_greet(event: InteractionEvent):
    """Respond to /greet"""
    if not isinstance(event.data, ApplicationCommandDataModel):
        return # ignore non-command interactions

    if event.data.name != "greet":
        return

    await client.interaction(event.id, event.token).respond("Hello!")

client.add_event_listener(EventType.INTERACTION_CREATE, on_greet)

# --- Run the bot ---
client.run()

About guild commands

Guild commands appear instantly when registered to a guild.

Global commands can take up to 1 hour to propagate.

Component Interactions


Demonstrates building and responding to a button interaction.

# --- Core library imports ---
from scurrypy import Client

from scurrypy.api.commands import SlashCommandPart
from scurrypy.api.interactions import ApplicationCommandDataModel, MessageComponentDataModel
from scurrypy.api.components import ActionRow, Button, ButtonStyle
from scurrypy.api.messages import MessagePart

from scurrypy.resources import Interaction
from scurrypy.events import EventType, InteractionEvent

# --- Setup bot ---
client = Client(token=TOKEN)

async def register_commands():
    """Register slash commands on startup (before READY)."""
    await client.guild_command(APP_ID, GUILD_ID).create(
        SlashCommandPart('button_demo', 'A command with a button!')
    )

client.add_startup_hook(register_commands)

async def handle_command(interaction: Interaction, data: ApplicationCommandDataModel):
    """Handle button_demo slash command."""

    if data.name != 'button_demo':
        return

    await interaction.respond(
        MessagePart(
            content='Press the button!',
            components=[
                ActionRow([
                    Button(ButtonStyle.PRIMARY, 'btn_demo', 'Press me!')
                ])
            ]
        )
    )

async def handle_button(interaction: Interaction, data: MessageComponentDataModel):
    """Handle btn demo custom ID."""

    if data.custom_id != 'btn_demo':
        return

    await interaction.update(content="You pressed the button!", components=[])

async def dispatch(event: InteractionEvent):
    """InteractionEvent is automatically resolved into a concrete interaction type."""

    interaction = client.interaction(event.id, event.token)
    data = event.data

    if isinstance(event.data, ApplicationCommandDataModel):
        await handle_command(interaction, data)
    elif isinstance(event.data, MessageComponentDataModel):
        await handle_button(interaction, data)

client.add_event_listener(EventType.INTERACTION_CREATE, dispatch)

# --- Run the bot ---
client.run()

Need to Refresh Your Commands?

Consider deleting your old commands before registering new ones:

async def refresh_commands():
    commands = await client.command(APP_ID, GUILD_ID).fetch_all()

    for cmd in commands:
        await client.command(APP_ID, GUILD_ID, cmd.id).delete()

    # then create your new commands

client.add_startup_hook(refresh_commands)
...
If you are using scurrypy.ext, this should be done for you.

Stateful Bot


Demonstrates grouping state and behavior using a class-based addon.

# --- Core library imports ---
from scurrypy import Client
from scurrypy.api.commands import SlashCommandPart
from scurrypy.api.interactions import ApplicationCommandDataModel
from scurrypy.events import EventType, InteractionEvent

import asyncio

class StatefulBot:
    def __init__(self, client: Client):
        self.bot = client

        # user registry
        self.user_points = {}
        self.dict_lock = asyncio.Lock()

        # commands registry
        self.commands = {
            'points': self.on_points,
            'addpoints': self.on_add_points
        }

        # hook up addon to client
        client.add_startup_hook(self.register_commands)
        client.add_event_listener(EventType.INTERACTION_CREATE, self.dispatch)

    async def register_commands(self):
        """Register slash commands on startup (before READY)."""
        bot_commands = self.bot.guild_command(APP_ID, GUILD_ID)
        commands = [
            SlashCommandPart('points', 'Check your points'),
            SlashCommandPart('addpoints', 'Give points')
        ]

        for cmd in commands:
            await bot_commands.create(cmd)

    async def on_points(self, event: InteractionEvent):
        """Get points for the invoking user."""

        user_id = event.member.user.id

        async with self.dict_lock:
            pts = self.user_points.get(user_id, 0)

        await self.bot.interaction(event.id, event.token).respond(f"You have {pts} points!")

    async def on_add_points(self, event: InteractionEvent):
        """Add points for the invoking user."""

        user_id = event.member.user.id

        async with self.dict_lock:
            self.user_points[user_id] = self.user_points.get(user_id, 0) + 1

        await self.bot.interaction(event.id, event.token).respond("Point added!")

    async def dispatch(self, event: InteractionEvent):
        """Main entry point for commands."""

        if not isinstance(event.data, ApplicationCommandDataModel):
            return # ignore non-command interactions

        handler = self.commands.get(event.data.name)

        if not handler:
            await self.bot.interaction(event.id, event.token).respond(f"No command named '{event.data.name}'!")
            return

        await handler(event)

client = Client(token=TOKEN)

# attach addon to client
StatefulBot(client)

# --- Run the bot ---
client.run()