Message Components

Message components allow you to include clickable buttons and dropdown menus in messages from your bot. The documentation on Discord covers their general use.

To use these components with Flask-Discord-Interactions, you can use the ActionRow, Button, and SelectMenu classes.

Select Menus

Select Menus will sent an Interaction when a user makes a selection. They function similarly to Buttons.

The Context object passed to your custom ID handler function will receive an additional property: Context.values. This property contains a list of the values that the user chose.

Defining each individual option can get quite verbose, so an example of this component is not provided here. However, one is provided in the Examples directory.

Storing State

It’s possible to include some state information inside the custom ID. This is a good way to keep your application stateless, which helps with scaling. However, the total length of the ID must be less than 100 characters, including the actual handler ID and any additional state you pass along. If you find yourself exceeding this limit, you should probably be storing that information in a database.

The Discord docs state that:

This field is a string of max 100 characters, and can be used flexibly to maintain state or pass through other important data.

Flask-Discord-Interactions provides a mechanism to uniquely identify custom handlers while allowing additional state information to “come along for the ride.” Simply pass a list in for the custom_id field on the button object.

@discord.custom_handler()
def handle_stateful(ctx, interaction_id, current_count: int):
    current_count += 1

    return Message(
        content=(f"This button has been clicked {current_count} times. "
                "Try calling this command multiple times to see--each button "
                "count is tracked separately!"),
        components=[
            ActionRow(components=[
                Button(
                    style=ButtonStyles.PRIMARY,
                    custom_id=[handle_stateful, interaction_id, current_count],
                    label="Click Me!"
                )
            ])
        ],
        update=True
    )

@discord.command()
def stateful_click_counter(ctx):
    "Count the number of button clicks for this specific button."

    return Message(
        content=f"Click the button!",
        components=[
            ActionRow(components=[
                Button(
                    style=ButtonStyles.PRIMARY,
                    custom_id=[handle_stateful, ctx.id, 0],
                    label="Click Me!"
                )
            ])
        ]
    )

This example passes two additional values as state: the ID of the interaction, and the current count of the button.

All values will be converted to a string before including them in the custom ID. However, to automatically convert them back to a bool or int, you can include a type annotation in the handler function, such as in the example above (current_count: int).

The pagination example demonstrates more sophisticated use of this technique to allow a user to jump between multiple pages.

Custom ID Internals

Custom IDs are used to uniquely identify a button. Thus, when a web server receives an incoming interaction, it can use the custom ID to determine what button triggered it.

Discord allows Custom IDs to be any string that is 100 characters or less (relevant docs). However, you may have noticed that the snippets above do not explicitly specify a custom ID string. Instead, the handler function appears to be passed directly to the custom_id field. How is this possible?

The trick is, the DiscordInteractions.custom_handler() decorator will actually generate a custom ID string (a uuid.uuid4()). It will return this custom ID string in place of the function, after it adds it to the internal DiscordInteractions.custom_id_handlers attribute. This means that the line custom_id=handle_click is actually passing a string to the Button constructor.

The reason behind this trickery is to abstract away the details of custom IDs from the developer. Buttons will just map directly to their handler functions. However, if you want more control, there is an escape hatch: the DiscordInteractions.custom_handler() decorator accepts a custom_id parameter which will override the ID given. Alternatively, you can use the DiscordInteractions.add_custom_handler() function to avoid the decorator syntax entirely.

Warning

This strategy works great for development, but can lead to some frustrating behavior in production:

  • Every time your app is restarted, old custom handlers will no longer function. This is likely desirable in development, but can cause issues in production.

  • If you deploy multiple instances/workers of your application, then they will not share the same custom IDs. This can lead to many issues, such as failure for one worker to process Interactions related to messages sent on another worker.

To avoid these issues, it is recommended that you specify a specific custom ID in these scenarios.

Additionally, Flask-Discord-Interactions needs to separate this handler ID from any additional state that needs to be preserved in the custom ID. To accomplish this, newlines are inserted into the custom ID string between the handler and any subsequent values. Later, when the custom ID is received in an incoming interaction, it is split on the newline character. The first line is used as the ID, and any subsequent lines are taken as arguments to the handler function.

Context Internals

Just like commands, custom_id handlers are passed a Context object when invoked. This object will also have a Context.message field containing the Message that contained the component.

Full API

class flask_discord_interactions.Component

Represents a Message Component.

dump()

Returns this Component as a dictionary, removing fields which are None.


class flask_discord_interactions.ComponentType
ACTION_ROW = 1
BUTTON = 2
SELECT_MENU = 3
TEXT_INPUT = 4

class flask_discord_interactions.ActionRow(**kwargs)

Bases: Component

Represents an ActionRow message component.

Parameters:

components (List[Component]) – The message components to display in the action row. Limited to any of the following: - 5 Buttons - 1 Select Menu

components: List[CustomIdComponent] = None
static from_dict(data)
type: int = 1

class flask_discord_interactions.Button(**kwargs)

Bases: CustomIdComponent

Represents a Button message component.

Parameters:
  • style (int) – The style of the button (see ButtonStyles).

  • label (str) – The label displayed on the button.

  • url (str) – For link buttons, the URL that the button links to.

  • disabled (bool) – Whether the button is disabled.

disabled: bool = False
emoji: dict = None
label: str = None
style: int = 1
type: int = 2
url: str = None

class flask_discord_interactions.ButtonStyles

Represents the styles that can be applied to Button message components.

PRIMARY = 1
SECONDARY = 2
SUCCESS = 3
DANGER = 4

class flask_discord_interactions.SelectMenu(**kwargs)

Bases: CustomIdComponent

Represents a SelectMenu message component.

Parameters:
  • options (List[SelectMenuOption]) – The options to display in the select menu.

  • placeholder (str) – The placeholder displayed when the select menu is empty.

  • min_values (int) – The minimum number of options that must be selected.

  • max_values (int) – The maximum number of options that can be selected.

  • disabled (bool) – Whether the select menu is disabled.

  • values (list) – Selected options. Only present when receiving components from a modal.

disabled: bool = False
max_values: int = 1
min_values: int = 1
options: List[SelectMenuOption] = None
placeholder: str = None
type: int = 3
values: list = None

class flask_discord_interactions.SelectMenuOption(**kwargs)

Bases: object

Represents an option in a SelectMenu message component.

Parameters:
  • label (str) – The label displayed on the option.

  • value (str) – The value of the option passed to the custom ID handler.

  • description (str) – The description displayed on the option.

  • emoji (dict) – The emoji displayed on the option.

  • default (bool) – Whether the option is the default option.

default: bool = False
description: str = None
emoji: dict = None
label: str
value: str