Custom Commands docsCustom CommandsDocs

Search docs

Find a page, function or guide

HomeSupportDashboard

Getting started

  • Introduction
  • Your first command
  • How a command runs
  • Variables in replies
  • Text commands
  • Events
  • Using Slushy

Guides

  • Welcome messages
  • Role menus
  • Auto moderation
  • Social alerts
  • Moderation commands
  • Levels and XP
  • Economy and coins
  • Ticket system
  • Giveaways
  • Poll
  • Button game
  • Reminders

Concepts

  • Buttons and menus
  • Forms
  • Saving data
  • Permissions and cooldowns
  • Limits and premium
  • Common mistakes

API reference

  • Response
  • Messages
  • Embeds
  • Members and roles
  • Channels
  • Storage
  • Checks
  • Views
  • Components
  • Modals
  • Interactions
  • Paginators
  • Web and files
  • Scheduling
  • Server events and polls
  • Time
  • Waiting
  • Utilities
  • Objects
  • Event variables

Examples

  • /8ball
  • /coinflip
  • /avatar
  • /serverinfo
  • /rank
  • /remind

Concepts

View .md

Buttons and menus

Two ways to add buttons and select menus, and how to pick the right one.

There are two ways to make buttons and menus work. Picking the right one saves a lot of code.

ViewComponent + event
Lives inThe command itselfA separate event
Works forA few minutes after the commandForever, even after the bot restarts
Can see the command's variablesYesNo
Good forConfirm prompts, games, pickers, quizzesRole panels, ticket buttons, "click any time" panels

Rule of thumb: if the click happens soon after the command, use a View. If the button must keep working for days, use a component and an event.

Views#

A View is a group of buttons and menus inside the command. You write what each button does right there, next to the rest of the command.

Pre-hook:

python
class Confirm(View):
    def __init__(self, user_id):
        self.user_id = user_id   # only this person can click
        self.choice = None

    @button(label="Yes", style="success", custom_id="yes")
    async def yes(self, interaction, component):
        await defer(interaction, action="update")
        self.choice = "yes"
        self.disable_all_components()
        await self.update_view()
        self.stop()

    @button(label="No", style="danger", custom_id="no")
    async def no(self, interaction, component):
        await defer(interaction, action="update")
        self.choice = "no"
        self.disable_all_components()
        await self.update_view()
        self.stop()

view = Confirm(user.id)
view.interaction = interaction
set_view(view)
set_content("Are you sure?")

Post-hook:

python
if view.choice == "yes":
    send_message(channel, "Confirmed.")
elif view.choice is None:
    send_message(channel, "Timed out.")

The post-hook runs after the View stops, or after it times out.

Things to know:

  • Start every button with await defer(interaction, action="update"). It tells Discord the click was received.
  • user_id limits who can click. Leave it out and anyone can click.
  • self.stop() ends the View.
  • await self.update_view() redraws the buttons, for example after you disable them.
  • The View times out after 3 minutes with no clicks.
  • Never call super().__init__().
  • With set_view, the bot waits for the View for you. Do not call view.wait().

Full list: Views.

Reading what was picked#

In a menu callback, read the choice from interaction.values. It is always a list.

python
@text_menu(
    options=[TextOption(label="Red", value="red"), TextOption(label="Blue", value="blue")],
    custom_id="color",
)
async def color(self, interaction, component):
    await defer(interaction, action="update")
    self.picked = interaction.values[0]
    self.stop()

Watch out

component is the menu you wrote, not what the user picked. component.values does not hold the choice. Use interaction.values.

Components and events#

For a button that must work forever, split it in two:

  1. A command posts the button with a custom_id.
  2. An event listens for that custom_id and does the work.

Command pre-hook:

python
c = ComponentBuilder()
c.add_button("Open ticket", style="primary", custom_id="ticket_open")
set_components(c)
set_content("Need help? Press the button.")

Event, type Button Click / Select Menu Change, Custom ids: ticket_open:

python
defer_response(ephemeral=True)
respond_interaction(interaction, "Your ticket is being made…")

The event does not know anything from the command. It only gets custom_id, user, channel, message and interaction.

Full list: Components.

The 3-second rule#

Discord waits 3 seconds for an answer to a click. Without one, the user sees "This interaction failed".

  • In a View button, start with await defer(interaction, action="update").
  • In an event, start with defer_response(...).

Then do the slow work.

See Interactions.

Discord limits#

  • At most 5 rows.
  • At most 5 buttons in a row.
  • A select menu takes a whole row by itself.
PreviousRemindersNext Forms

On this page

  • Views
  • Reading what was picked
  • Components and events
  • The 3-second rule
  • Discord limits
Custom CommandsCustom Commands

The #1 Custom Commands Discord bot — build commands, events and databases with zero boilerplate.

Join the support server

Product

  • Overview
  • Why us?
  • Premium
  • Template shop

Features

  • AI Agent
  • Reaction roles
  • Welcome messages
  • Embed builder
  • Social alerts
  • Custom bot

Resources

  • Docs
  • Blog
  • Support
  • Dashboard

Company

  • Privacy
  • Terms
  • Contact
contact@ccbot.app

2 Frederick Street, London, WC1X 0ND

Payments secured by Stripe

© WEiRDSOFT LTD. All rights reserved.

Custom Commands