glyde/model/component

Message components: action rows, buttons and select menus.

Two positions, two types. A Component is a top-level entry on a message: an action row, or a Components V2 element. A RowChild is what sits inside a row: a button, a select menu. Discord answers 400 to a row nested in a row and to a button at top level, and neither is a value that can be built here.

Types 1, 2, 3, 5, 6, 7 and 8 are modelled. Everything else keeps its payload intact, so another bot’s Components V2 message does not sink your MESSAGE_CREATE.

Types

A link button with a custom_id is a 400, so each kind carries only the fields its style allows. Styles 5 and 6 are the link and premium buttons.

pub type ButtonKind {
  ActionButton(
    custom_id: String,
    style: ButtonStyle,
    label: ButtonLabel,
  )
  LinkButton(url: String, label: ButtonLabel)
  PremiumButton(sku_id: id.Id(id.Sku))
}

Constructors

  • ActionButton(
      custom_id: String,
      style: ButtonStyle,
      label: ButtonLabel,
    )
  • LinkButton(url: String, label: ButtonLabel)
  • PremiumButton(sku_id: id.Id(id.Sku))

    Discord supplies the label from the SKU, so this one takes none.

Discord needs a button to say something, so “no label and no emoji” is not a state to have. Text is max 80 characters.

pub type ButtonLabel {
  Text(String)
  Icon(emoji.Emoji)
  TextAndIcon(String, emoji.Emoji)
}

Constructors

pub type ButtonStyle {
  PrimaryButton
  SecondaryButton
  SuccessButton
  DangerButton
  UnknownButtonStyle(Int)
}

Constructors

  • PrimaryButton
  • SecondaryButton
  • SuccessButton
  • DangerButton
  • UnknownButtonStyle(Int)

A top-level entry in a message’s components.

pub type Component {
  ActionRow(id: option.Option(Int), components: List(RowChild))
  UnknownComponent(type_: Int, raw: RawPayload)
}

Constructors

  • ActionRow(id: option.Option(Int), components: List(RowChild))

    Type 1. Holds up to five buttons, or exactly one select menu.

    Arguments

    id

    Discord’s per-component sequence number, not a snowflake.

  • UnknownComponent(type_: Int, raw: RawPayload)

    Types 9 to 23, the Components V2 elements, and anything else that arrived where a row belongs. The raw payload is kept, so it can be sent back unchanged.

Discord’s type number, the one field both positions carry. Types 9 to 23 are Components V2, unmodelled, and arrive as UnknownComponentType.

pub type ComponentType {
  ActionRowType
  ButtonType
  StringSelectType
  TextInputType
  UserSelectType
  RoleSelectType
  MentionableSelectType
  ChannelSelectType
  UnknownComponentType(Int)
}

Constructors

  • ActionRowType
  • ButtonType
  • StringSelectType
  • TextInputType

    A modal’s text input, one per action row.

  • UserSelectType
  • RoleSelectType
  • MentionableSelectType
  • ChannelSelectType
  • UnknownComponentType(Int)

A pre-selected entry on an entity select, tagged so id and kind agree.

pub type DefaultValue {
  DefaultUser(id: id.Id(id.User))
  DefaultRole(id: id.Id(id.Role))
  DefaultChannel(id: id.Id(id.Channel))
}

Constructors

A payload this build does not model, read once into keys and values. The read happens at decode time, where a malformed payload is expected, so nothing downstream has to handle a failure by dropping the lot.

pub opaque type RawPayload

What an action row holds. Never a row: Discord answers 400 to a nested one, and never a Components V2 element, which is top level.

pub type RowChild {
  Button(
    id: option.Option(Int),
    kind: ButtonKind,
    disabled: Bool,
  )
  SelectMenu(
    id: option.Option(Int),
    custom_id: String,
    kind: SelectKind,
    placeholder: option.Option(String),
    min_values: option.Option(Int),
    max_values: option.Option(Int),
    disabled: Bool,
  )
  UnknownChild(type_: Int, raw: RawPayload)
}

Constructors

  • Button(id: option.Option(Int), kind: ButtonKind, disabled: Bool)

    Type 2. What it says is in kind: a premium button takes its label from the SKU, and the other two need one of their own.

  • SelectMenu(
      id: option.Option(Int),
      custom_id: String,
      kind: SelectKind,
      placeholder: option.Option(String),
      min_values: option.Option(Int),
      max_values: option.Option(Int),
      disabled: Bool,
    )

    Types 3, 5, 6, 7 and 8. kind is the discriminator.

    Arguments

    custom_id

    1 to 100 characters, chosen by you.

  • UnknownChild(type_: Int, raw: RawPayload)

    Type 4, the text input a modal puts in a row, and a type 2 with no kind to degrade to: a button Discord would reject. The raw payload is kept, so it can be sent back unchanged.

pub type SelectKind {
  StringSelect(options: List(SelectOption))
  UserSelect(default_values: List(DefaultValue))
  RoleSelect(default_values: List(DefaultValue))
  MentionableSelect(default_values: List(DefaultValue))
  ChannelSelect(
    channel_types: List(channel.ChannelType),
    default_values: List(DefaultValue),
  )
}

Constructors

  • StringSelect(options: List(SelectOption))

    Type 3. Carries 1 to 25 options.

  • UserSelect(default_values: List(DefaultValue))

    Type 5.

  • RoleSelect(default_values: List(DefaultValue))

    Type 6.

  • MentionableSelect(default_values: List(DefaultValue))

    Type 7. Resolves to users and roles together.

  • ChannelSelect(
      channel_types: List(channel.ChannelType),
      default_values: List(DefaultValue),
    )

    Type 8.

    Arguments

    channel_types

    Empty means every channel type is offered.

pub type SelectOption {
  SelectOption(
    label: String,
    value: String,
    description: option.Option(String),
    emoji: option.Option(emoji.Emoji),
    default: Bool,
  )
}

Constructors

  • SelectOption(
      label: String,
      value: String,
      description: option.Option(String),
      emoji: option.Option(emoji.Emoji),
      default: Bool,
    )

    Arguments

    label

    Max 100 characters, shown to the user.

    value

    Max 100 characters, sent back to you on click.

Values

pub fn button(
  custom_id custom_id: String,
  label label: String,
) -> RowChild

PrimaryButton is our default, not Discord’s: the API requires a style and has none of its own. Change it with a record update.

pub fn button_style_decoder() -> decode.Decoder(ButtonStyle)
pub fn button_style_from_int(value: Int) -> ButtonStyle
pub fn button_style_to_int(value: ButtonStyle) -> Int
pub fn button_style_to_json(value: ButtonStyle) -> json.Json
pub fn component_type(component: Component) -> ComponentType
pub fn component_type_from_int(value: Int) -> ComponentType
pub fn component_type_to_int(value: ComponentType) -> Int
pub fn default_value_decoder() -> decode.Decoder(DefaultValue)

An unrecognised type fails the whole select menu on purpose: a default dropped silently would pre-select the wrong thing.

pub fn default_value_to_json(value: DefaultValue) -> json.Json
pub fn empty_raw_payload() -> RawPayload
pub fn link_button(
  url url: String,
  label label: String,
) -> RowChild

Opens a url, and sends no interaction.

pub fn option(
  label label: String,
  value value: String,
) -> SelectOption
pub fn premium_button(sku_id: id.Id(id.Sku)) -> RowChild

A premium upsell. Discord supplies the label from the SKU.

pub fn raw_payload(
  value: dynamic.Dynamic,
) -> Result(RawPayload, Nil)

Error(Nil) when the value is not a JSON object. Decoders fall back to empty_raw_payload; nothing else needs to.

pub fn raw_payload_entries(
  payload: RawPayload,
) -> List(#(String, json.Json))

The keys as they will be written, sorted: a Dict has no order of its own and these become request bytes.

pub fn row_child_decoder() -> decode.Decoder(RowChild)
pub fn row_child_to_json(child: RowChild) -> json.Json
pub fn row_child_type(child: RowChild) -> ComponentType
pub fn rows(children: List(RowChild)) -> List(Component)

Pack loose children into action rows: five buttons to a row, a whole row per select menu, order preserved. A row and a Components V2 element are top level, so they are not inputs here: append them to what this returns.

pub fn select(
  custom_id custom_id: String,
  kind kind: SelectKind,
) -> RowChild
pub fn select_option_decoder() -> decode.Decoder(SelectOption)
pub fn select_option_to_json(item: SelectOption) -> json.Json
pub fn to_json(component: Component) -> json.Json
Search Document