Skip to content

Examples


The following examples use the scurrypy.ext module.

Basic Event


Demonstrates responding to the READY event.

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

from scurrypy.ext.events import EventsAddon

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

@events.listen(EventType.READY)
async def on_ready(bot: Client, event: ReadyEvent):
    print(f"{event.user.username} is online!")

# --- 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.ext.prefixes import PrefixAddon, PrefixCommandContext

client = Client(token=TOKEN, intents=Intents.DEFAULT | Intents.MESSAGE_CONTENT)
prefixes = PrefixAddon(client, APP_ID, '!')

# --- Setup bot ---
@prefixes.listen('ping')
async def on_ping(ctx: PrefixCommandContext):
    await ctx.send("Pong!")

# --- 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.ext.commands import CommandsAddon, ApplicationCommandContext

# --- Setup bot ---
client = Client(token=TOKEN)
commands = CommandsAddon(client, APP_ID)

@commands.slash_command('greet', 'Greet the bot!', guild_ids=[GUILD_ID])
async def on_greet(ctx: ApplicationCommandContext):
    await ctx.respond("Hello!")

# --- 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.components import ActionRow, Button, ButtonStyle
from scurrypy.api.messages import MessagePart

from scurrypy.ext.commands import CommandsAddon, ApplicationCommandContext
from scurrypy.ext.components import ComponentsAddon, MessageComponentContext

# --- Setup bot ---
client = Client(token=TOKEN)
commands = CommandsAddon(client, APP_ID)
components = ComponentsAddon(client)

@commands.slash_command('button_demo', 'Button demo!', guild_ids=[GUILD_ID])
async def handle_command(ctx: ApplicationCommandContext):
    await ctx.respond(
        MessagePart(
            content='Press the button!',
            components=[
                ActionRow([
                    Button(ButtonStyle.PRIMARY, 'btn_demo', 'Press me!')
                ])
            ]
        )
    )

@components.button('btn_demo')
async def handle_button(ctx: MessageComponentContext):
    await ctx.update(content="You pressed the button!", components=[])

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

Stateful Bot


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

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

from scurrypy.ext.commands import CommandsAddon, ApplicationCommandContext

# --- Setup bot and addons ---
client = Client(token=TOKEN)
commands = CommandsAddon(client, APP_ID)

user_points = {}

import asyncio
dict_lock = asyncio.Lock()

@commands.slash_command("points", "Check your points", guild_ids=[GUILD_ID])
async def points(ctx: ApplicationCommandContext):
    async with dict_lock:
        pts = user_points.get(ctx.user.id, 0)

    await ctx.respond(f"You have {pts} points!")

@commands.slash_command("addpoints", "Give points", guild_ids=[GUILD_ID])
async def addpoints(ctx: ApplicationCommandContext):
    async with dict_lock:
        user_points[ctx.user.id] = user_points.get(ctx.user.id, 0) + 1

    await ctx.respond("Point added!")

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

Handling Command Errors


Demonstrates handling invalid user input inside a prefix command.

Tip

By default, ScurryPy catches unhandled command errors and logs a traceback. The bot will not crash.

from scurrypy import Client, Intents
from scurrypy.ext.prefixes import PrefixAddon, PrefixCommandContext

client = Client(token=TOKEN, intents=Intents.DEFAULT | Intents.MESSAGE_CONTENT)
prefixes = PrefixAddon(client, APP_ID, '!')

@prefixes.listen('divide')
async def divide(ctx: PrefixCommandContext):
    try:
        a = int(ctx.args[0])
        b = int(ctx.args[1])
        await ctx.send(f"{a}/{b} = {a/b:.2f}")
    except ZeroDivisionError:
        await ctx.send("The second value cannot be zero!")
    except IndexError:
        await ctx.send("This command requires 2 numbers! Try `!divide 6 3`")
    except ValueError:
        await ctx.send("Command args must be integers!")

client.run()

Role Restricted Command


Demonstrates a command needing a certain role to use.

from scurrypy import Client
from scurrypy.ext.commands import CommandsAddon, ApplicationCommandContext

# Set EXCLUSIVE_ROLE_ID to ID of role in which command is available.

client = Client(token=TOKEN)
commands = CommandsAddon(client, APP_ID)

@commands.slash_command('exclusive', 'Only certain roles can use this command!', guild_ids=[GUILD_ID])
async def on_exclusive(ctx: ApplicationCommandContext):
    if not EXCLUSIVE_ROLE_ID in ctx.member.roles:
        await ctx.respond(f"You need the <@&{EXCLUSIVE_ROLE_ID}> role to use this command!", ephemeral=True)
        return

    # ephemeral because its a secret!
    await ctx.respond("Welsh Myth Secret: Corgis were once ridden by faries!", ephemeral=True)

client.run()

Background Task


ScurryPy uses standard asyncio tasks for background jobs.

from scurrypy import Client

client = Client(token=TOKEN)

import asyncio

async def my_task():
    while True:
        print("Running background task...")
        await asyncio.sleep(5)

async def start_my_task():
    asyncio.create_task(my_task())

client.add_startup_hook(start_my_task)

client.run()

Modals


Demonstrates submitting feedback with the modal.

from scurrypy import Client
from scurrypy.api.interactions import ModalPart
from scurrypy.api.components import Label, TextInput, TextInputStyle

from scurrypy.ext.commands import CommandsAddon, ApplicationCommandContext
from scurrypy.ext.components import ComponentsAddon, ComponentModalContext

client = Client(token=TOKEN)
commands = CommandsAddon(client, APP_ID)
components = ComponentsAddon(client)

@components.modal("feedback_modal")
async def on_feedback(ctx: ComponentModalContext):
    feedback = ctx.data.get_modal_data("feedback_input")
    await ctx.respond(f"Thanks for your feedback:\n{feedback}", ephemeral=True)

@commands.slash_command("feedback", "Send feedback", guild_ids=[GUILD_ID])
async def feedback(ctx: ApplicationCommandContext):

    feedback_input = Label(
        label="Your feedback",
        component=TextInput(
            custom_id="feedback_input",
            style=TextInputStyle.PARAGRAPH,
            placeholder="Type something...",
            required=True
        )
    )

    modal = ModalPart(
        title="Feedback Form",
        custom_id="feedback_modal",
        components=[feedback_input]
    )

    await ctx.respond_modal(modal)

client.run()

More Examples...


Want more control? Check out core examples.

Want more customization? Check out using Addons.

Ready for a challenge? Check out advanced patterns.