the module - Bilal2453/discordia-components GitHub Wiki

After installing this extension and to begin using it, you will have to require it through require("discordia-components"). Once you do, Discordia will be patched to include all of the utilities needed to construct, send and receive the components.

Those fields are both patched into discordia's init and returned by this module on require.

Field Type
Components Components
Button Button
SelectMenu SelectMenu

There are two ways to access the library's constructors, first one is to directly using discordia module, while the second is to use the returned table by requiring this library:

local discordia = require("discordia")
require("discordia-components")
local Components = discordia.Components

vs

local discordia_components = require("discordia-components")
local Components = discordia_components.Components

Most of the examples will use the former, since I personally prefer it, plus you don't have to create yet another local.

The main builder of this library is the Components class, although you can as well directly construct a Button or a SelectMenu. Following examples all result in the same final output:

Using methods constructors with table inputs:

local music_controls = discordia.Components()
  :button {
    id = "next",
    label = "Next Song",
    style = "danger",
  }
  :selectMenu {
    id = "song_chooser",
    placeholder = "Select a Song",
    options = {
      {label = "Cool Song", value = "song_1"},
      {label = "Good Song", value = "song_2"},
    }
  }

Using tables for all constructors:

local music_controls = discordia.Components {
  {
    type = "button",
    id = "next",
    label = "Next Song",
    style = "danger",
  },
  {
    type = "selectMenu",
    id = "song_chooser",
    placeholder = "Select a Song",
    options = {
      {label = "Cool Song", value = "song_1"},
      {label = "Good Song", value = "song_2"},
    }
  }
}

Using methods for all constructors:

local next_song = discordia.Button("next")
  :label "Next Song"
  :style "danger"
local song_chooser = discordia.SelectMenu('song_chooser')
  :placeholder "Select a Song"
  :option("Cool Song", "song_1")
  :option("Good Song", "song_2")
local music_controls = discordia.Components()
  :button(next_song)
  :selectMenu(song_chooser)

In the above example, the last step can also be a table:

local music_controls = discordia.Components {
  next_song, song_chooser
}

therefore, can be simplified to:

local music_controls = discordia.Components {
  discordia.Button("next")
    :label "Next Song"
    :style "danger",
  discordia.SelectMenu("song_chooser")
    :placeholder "Select a Song"
    :option("Cool Song", "song_1")
    :option("Good Song", "song_2"),
}

Sending the components is achieved with TextChannel:sendComponents() or its sugar Message:replyComponents(), we will be using the latter here but both work the same exact way.

Considering the above examples for constructing the components. Sending the previously constructed music_controls:

client:on("messageCreate", function(msg)
  if msg.content == "!music" then
    msg:replyComponents("Here your controls!", music_controls)
  end
end)

Sending a component without constructing a Components instance:

msg:replyComponents("Here your song chooser!", song_chooser)

The first argument of sendComponents/replyComponents is identical to the one used in Discordia's send/reply:

msg:replyComponents({
  embed = {
    title = "Current Song",
    description = "Cool Song 1",
    color = 0xf0f0f0,
  },
  file = "path/to/file.png", -- identical to discordia's
}, next_song)

You can as well update a message's components (Use Interaction:update() anytime you can instead of this):

next_button:label "Previous Song" -- any changes on this object are linked with music_controls instance
msg:updateComponents(music_controls, "Here a previous song button instead!")

And also remove all components (Also use Interaction:update() when possible for this scenario):

msg:updateComponents(false, "Hah! I took your music controls away!")

Receiving the components is as easy, though since the library does not implement any event for it, you mainly have two ways, the first is using the provided waitComponent to yield current coroutine (similar to client.waitFor) until a button is pressed; which is probably what you want most of the time. Or listening on interactionCreate event and filtering the inputs and outputs you desire.

Using Message:waitComponent() method:

client:on("messageCreate", function(msg)
  if msg.content == "!music" then
    local comp_msg = msg:replyComponents("Music controls for day!", music_control)
    local _, intr = comp.msg:waitComponent("button", "next_song") -- tell it that we are waiting for a button its custom_id is next_song
    -- intr is the Interaction object, you shall respond to this as fast as you can
    intr:update{
      components = {}, -- remove all components
      content = "Oops! I dropped the controls.",
    }
  end
end)

Using interactionCreate event (you will probably want this when you want to respond to a component forever):

-- Once the bot is ready, send a button in specific channel
client:once("ready", function()
  local channel = client:getChannel("ID")
  channel:sendComponent("Click the button to assign you a role", some_button)
end)

-- Listen to all interactions, including commands and components
client:on("interactionCreate", function(intr)
  if intr.type == discordia.enums.componentType.button then -- is it a button?
    if intr.data.custom_id == "some_button_id" then -- is it the button we want?
      intr:replyDeferred(true) -- show an ephemeral progress to the user while we do stuff
      local success, err = intr.member:addRole("ID")
      if not success then
        intr:reply("Attempt to assign you a role: " .. err) -- this will an ephemeral reply
      else
        intr:reply("You have been assigned a role")
      end
    end
  end
end)