Work In Progress
This documentation is in beta. It's missing lots of content, search is broken, and many links go nowhere. These problems will be fixed before release, but there's plenty of work left!
Skip to main content

Messages: Easy DSL

Messages support a specific set of components, which you can read about on the basics page.

The components DSL (based on the ComponentContainer class) is a convenient API for working with short-lived message components.

If you need your components to work after your bot restarts, we recommend using the manual handling approach.

No Atomic API

Kord Extensions doesn't provide an atomic API for modifying your components after you've sent them to Discord. This is mostly due to technical limitations within Kotlin's type system, and it is unlikely this will change any time soon.

If you wish to edit components, we suggest keeping a reference to the ComponentContainer returned by the components builder, and any relevant components that you create. The components within any given ComponentContainer may be applied to any message creation/editing builder via the applyComponents extension function.

Usage

To get started, use the components builder in any message creation or editing builder. This builder will automatically add the defined components to the message, and set up the code necessary for your bot to respond to them.

respond {
components {
publicButton {
label = "Button"
style = ButtonStyle.Primary

action {
respond {
content = "Clicked button."
}
}
}
}
}

The above example produces a message like the following:

Avatar
Kord ExtensionsAPP10:30
Button

For more information on what components you can add with this DSL, see the builder API section below.

Packing

The components DSL automatically packs its defined components as tightly as possible, while retaining the order you defined them in.

As an example, let's say you provided a select menu, six buttons, and another select menu. The resulting message would look like this:

Avatar
Kord ExtensionsAPP10:30
Button
Button
Button
Button
Button
Button

You can prevent this for specific components by specifying the row to add your component to — just pass an integer representing the row to the corresponding component builder function. This will add that component to the specified row immediately, and the builder will pack any components without defined rows around it before your bot finishes creating the message.

As an example, let's say you provided a select menu on row 2, six buttons without row numbers, and another select menu on row 3. The resulting message would look like this:

Avatar
Kord ExtensionsAPP10:30
Button
Button
Button
Button
Button
Button

Builder Functions

Entrypoint

components(...) { ... }Receiver: MessageBuilderFunction Returns: ComponentContainer

Convenience function for creating a ComponentContainer with components, and applying it to the message you're creating/editing.

Function Arguments
timeoutType: Duration?Default: null

Optional component interaction timeout. When nobody interacts with the components for the length of the timeout, Kord Extensions will unregister them and they'll stop working.


Buttons

disabledButton(...) { ... }Receiver: DisabledInteractionButtonFunction Returns: DisabledInteractionButton

Create a disabled button.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

ephemeralButton(...) { ... }Receiver: EphemeralInteractionButtonFunction Returns: EphemeralInteractionButton

Create an interactable button based on an ephemeral interaction flow.

Function Arguments
modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

linkButton(...) { ... }Receiver: LinkInteractionButtonFunction Returns: LinkInteractionButton

Create a link button.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

publicButton(...) { ... }Receiver: PublicInteractionButtonFunction Returns: PublicInteractionButton

Create an interactable button based on a public interaction flow.

Function Arguments
modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.


Select Menu (Channels)

ephemeralChannelSelectMenu(...) { ... }Receiver: EphemeralChannelSelectMenuFunction Returns: EphemeralChannelSelectMenu

Create an channel select menu based on an ephemeral interaction flow.

Function Arguments
modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

publicChannelSelectMenu(...) { ... }Receiver: PublicChannelSelectMenuFunction Returns: PublicChannelSelectMenu

Create an channel select menu based on a public interaction flow.

Function Arguments
modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.


Select Menu (Mentionables)

ephemeralMentionableSelectMenu(...) { ... }Receiver: EphemeralMentionableSelectMenuFunction Returns: EphemeralMentionableSelectMenu

Create a mentionable select menu based on an ephemeral interaction flow.

Function Arguments
modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

publicMentionableSelectMenu(...) { ... }Receiver: PublicMentionableSelectMenuFunction Returns: PublicMentionableSelectMenu

Create a mentionable select menu based on a public interaction flow.

Function Arguments
modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.


Select Menu (Roles)

ephemeralRoleSelectMenu(...) { ... }Receiver: EphemeralRoleSelectMenuFunction Returns: EphemeralRoleSelectMenu

Create a role select menu based on an ephemeral interaction flow.

modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

publicRoleSelectMenu(...) { ... }Receiver: PublicRoleSelectMenuFunction Returns: PublicRoleSelectMenu

Create a role select menu based on a public interaction flow.

modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.


Select Menu (Strings)

ephemeralStringSelectMenu(...) { ... }Receiver: EphemeralStringSelectMenuFunction Returns: EphemeralStringSelectMenu

Create a string select menu based on an ephemeral interaction flow.

modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

publicStringSelectMenu(...) { ... }Receiver: PublicStringSelectMenuFunction Returns: PublicStringSelectMenu

Create a string select menu based on a public interaction flow.

modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.


Select Menu (Users)

ephemeralUserSelectMenu(...) { ... }Receiver: EphemeralUserSelectMenuFunction Returns: EphemeralUserSelectMenu

Create a user select menu based on an ephemeral interaction flow.

modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

publicUserSelectMenu(...) { ... }Receiver: PublicUserSelectMenuFunction Returns: PublicUserSelectMenu

Create a user select menu based on a public interaction flow.

modalType: (() -> ModalForm)?Default: null

Optional callable returning the Modal form to present users with when they interact with this component.

Function Arguments
rowType: Int?Default: null

Optional action row the component should be placed into, if you don't want it to be packed automatically.

Container API

open class ComponentContainer : KordExKoinComponent

Class representing a single set of components to apply to any message.

Constructor Arguments
val timeoutType: Duration?Default: null

Optional timeout, after which all registered components will be unregistered and won't work anymore. This timeout is reset whenever a user interacts with a registered component.

startNowType: BooleanDefault: false

Whether to start the timeout immediately. This is usually set to true by the components { } DSL, but you can call timeoutTask.start() yourself in other situations.

Builders

onTimeout { ... }Receiver: ComponentContainer

Register a callback to be run when the timeout expires, if one was provided.

Component containers can only have one registered callback. Calling this function twice will overwrite the previous one.

Functions

open suspend cancel(...)

Cancel the timeout task and remove all stored components, unregistering them.

This behaviour is similar to letting the timeout expire, but it won't run the extra timeoutCallback provided by the onTimeout { } builder.

open suspend remove(...)

Remove a component from this container, and unregister it.

Arguments
componentType: Component

Component to remove.

open suspend removeAll(...)

Remove all components from this container, and unregister them.

open suspend replace(...)

Remove a component from this container, replacing it with a new one and likewise handling un/registration.

Arguments
oldTypes:ComponentString

The component to remove. You can provide the component object, or a String representing its ID.

newType: Component

The component to replace with.

open suspend sort(...)

Sort any unsorted components into rows, by packing them as tightly as possible.

Show/Hide Internal APIs
open suspend add(...)

Add a component to this container, and register it.

This is usually called by the builder functions.

Arguments
componentType: Component

Component to add.

rowNumType: Int?Default: null

Optional row to pack the component into.

If you provide a row number, the component will immediately be packed into the corresponding list in rows. Otherwise, it'll be added to unsortedComponents for the sort() function to handle later.

Properties

open val timeoutTaskType: Task?

Timeout task, responsible for enforcing the provided timeout. This will be null if no timeout was provided.

Show/Hide Internal APIs
open val rowsType: Array<MutableList<Component>>

Array representing up to five rows of sorted and packed components.

This array always contains five mutable lists, which are filled by the sort() function.

open var timeoutCallbackType: (suspend (ComponentContainer).() -> Unit)?Default: null

Extra callback to run when the timeout expires, if one was provided.

Usually set via the onTimeout { } builder.

open val unsortedComponentsType: MutableList<Component>

List containing all components without an explicitly defined row, to be packed into rows before adding the components to a message.

Component API

abstract class Component : KordExKoinComponent

Abstract class representing a Discord component.

open val unitWidthType: IntDefault: 1

How many units this component will take up.

abstract apply(...)

Apply this component to a Kord action row.

builderType: ActionRowBuilder
abstract validate(...)

Throws exceptions if this component isn't valid.

abstract class ComponentWithID : Component

Abstract class representing a Discord component with an ID.

open var idType: StringDefault: UUID.randomUUID()

Component's ID. Defaults to a random UUID, but you can overwrite this in your component builders.

abstract class ComponentWithAction : ComponentWithID, Lockable

Abstract class representing a Discord component which can respond to an interaction.

Type Parameters
EType: ComponentInteractionCreateEvent

Generic type representing the event type corresponding with the interactions this component can respond to.

CType: ComponentContext

Generic type representing the context object type that will be created when handling interactions for this component.

MType: ModalForm

Generic type representing the modal form type that will be sent in response to interactions for this component.

Constructor Arguments
timeoutTaskType: Task?

Timeout task, passed down from the component container. Will be null if no timeout was provided.

modalType: (() -> M)?Default: null

Callback returning a ModalForm object, as explained here.

action { ... }Receiver: C

Call this builder to register an action block, which will be run in response to a component interaction.

Lambda Arguments
modalType: M?

Modal form object, if a modal callback was provided when registering this component. Will be null if you don't provide that callback, or if the user doesn't submit the modal quickly enough.

check(...)

Register a check that must pass before the action can be run.

Arguments
vararg checksType: Check

Checks to add.

requireBotPermissions(...)

Register a set of permissions the bot will need to respond successfully to an interaction.

Arguments
vararg permsType: Permission

Permissions to add.

open var deferredAckType: BooleanDefault: true

Whether to use a deferred ack when responding to interactions. This will prevent Discord from showing a "Thinking..." message.

Buttons

abstract class InteractionButton : Component, HasPartialEmoji

Class representing a button component.

var labelType: Key?Default: null

Translated button label, for display on Discord. Optional if you provide an emoji.

abstract class InteractionButtonWithID : ComponentWithID, HasPartialEmoji

Class representing a button component that has an ID.

var labelType: Key?Default: null

Translated button label, for display on Discord. Optional if you provide an emoji.

abstract class InteractionButtonWithAction : ComponentWithAction, HasPartialEmoji

Class representing a button component that can respond to interactions.

var disabledType: BooleanDefault: false

Whether this button is disabled.

var labelType: Key?Default: null

Translated button label, for display on Discord. Optional if you provide an emoji.

disable(...)

Disable this button by setting disabled to true.

enable(...)

Enable this button by setting disabled to false.

Interaction Buttons

Ephemeral and public interaction buttons extend InteractionButtonWithAction, and provide some additional APIs.

open var styleType: ButtonStyleDefault: Primary

This button's style. Anything other than Link or Premium is valid here.

initialResponse { ... }Receiver: (suspend InteractionResponseCreateBuilder.(ButtonInteractionCreateEvent) -> Unit)?

Use this builder to respond to interactions with a message immediately, instead of using an empty acknowledgement.

Disabled Buttons

Disabled buttons extend InteractionButtonWithID, and provide some additional APIs.

open var styleType: ButtonStyleDefault: Primary

This button's style. Anything other than Link or Premium is valid here.

Link buttons extend InteractionButton, and provide some additional APIs.

open var urlType: String

URL to send the user to when they click/tap this button.

Select Menus

All ephemeral and public select menus extend the SelectMenu type below, along with an interface representing the type of data they handle.

abstract class SelectMenu : ComponentWithAction

Class representing a select menu component.

open var disabledType: Boolean?Default: null

Whether this select menu is disabled.

A true value here will disable the component. To enable the component, the value must be null instead, not false. We don't know why this is, unfortunately.

Consider using the disable() and enable() functions instead of setting this directly.

var maximumChoicesType: Int?Default: 1

The maximum number of selections a user can choose. Set to null for no maximum.

var minimumChoicesType: IntDefault: 1

The minimum number of selections a user can choose.

var placeholderType: Key?Default: null

Placeholder text to show when the user hasn't made a selection.

disable(...)

Disable this select menu by setting disabled to true.

enable(...)

Enable this select menu by setting disabled to null.

Channel Menus

interface ChannelSelectMenu
channelType(...)

Add one or more channel types to this select menu. This will limit which channels the user can select.

Arguments
vararg typesType: ChannelType

Channel types to add.

defaultChannel(...)

Add one or more pre-selected channels to this select menu. These channels will be selected by default.

Mentionable Menus

interface MentionableSelectMenu
defaultRole(...)

Add one or more pre-selected roles to this select menu. These roles will be selected by default.

defaultUser(...)

Add one or more pre-selected users to this select menu. These users will be selected by default.

Role Menus

interface RoleSelectMenu
defaultRole(...)

Add one or more pre-selected roles to this select menu. These roles will be selected by default.

String Menus

interface StringSelectMenu
option { ... }Receiver: StringSelectOption

Add an option to this select menu.

Function Arguments
labelType: Key

Translatable option label, shown to users on Discord.

valueType: String

String value, which is what you'll actually receive when users interact with the select menu.

class StringSelectOption : HasPartialEmoji
descriptionType: Key?Default: null

Translatable additional option description, shown to users on Discord.

defaultType: BooleanDefault: false

Whether this option should be selected by default.

User Menus

interface UserSelectMenu
defaultUser(...)

Add one or more pre-selected users to this select menu. These users will be selected by default.

Component Contexts

All component action { } blocks use a corresponding context object as the receiver, allowing you to retrieve any relevant data and respond to component interactions.

abstract class ComponentContext : KordExKoinComponent, TranslatableContext

Class representing a basic component context, with functionality common to all contexts.

Type Parameters
EType: ComoponentInteractionCreateEvent

Generic type representing the event that triggered this interaction.

Constructor Arguments
componentType: Component

Component object corresponding with this interaction.

eventType: E

Object representing the event that triggered this interaction.

cacheType: MutableMap<String, Any>

Data cache map shared with this command's checks.

open var channelType: MessageChannelBehavior

The channel this component interaction happened in.

open var guildType: GuildBehavior?

The guild this component interaction happened in, or null if it happened in a DM.

open var memberType: MemberBehavior?

The guild member that caused this component interaction, or null if this component interaction happened in a DM.

open var messageType: Message

The Discord message this component belongs to.

open var userType: UserBehavior

The Discord user that caused this component interaction.

Select Menus

Select menus use a context type extending the above, with some small additions:

  • Channel Menus:
    • A selected property containing the selected channels.
  • Mentionable Menus:
    • A selected property containing the IDs of all selected users and roles.
    • A selectedRoles property containing the selected roles.
    • A selectedUsers property containing the selected users.
  • Role Menus:
    • A selected property containing the selected roles.
  • String Menus:
    • A selected property containing the selected option strings.
  • String Menus:
    • A selected property containing the selected users.

Utilities

interface HasPartialEmoji

Interface representing components that support emojis.

emoji(...)

Set this component's emoji.

Arguments
emojiTypes:StringGuildEmojiReactionEmoji

Emoji to set.

MessageBuilder.applyComponents(...)

Convenience function for applying an arbitrary ComponentContainer to the message you're currently creating/editing.

Arguments
componentsType: ComponentContainer

ComponentContainer to apply.