Interactions
Lunibee provides typed interaction classes for every Discord interaction type. All interactions share a common base with lifecycle methods for replying, deferring, and following up.
Import
Section titled “Import”import { CommandInteraction, ComponentInteraction, ModalSubmitInteraction, AutocompleteInteraction, Interaction,} from "lunibee";import { CommandInteraction, ComponentInteraction, ModalSubmitInteraction, AutocompleteInteraction, Interaction,} from "lunibee";Common Properties (all types)
Section titled “Common Properties (all types)”| Property | Type | Description |
|---|---|---|
id |
string |
Interaction snowflake ID. |
applicationId |
string |
The application’s ID. |
token |
string |
Interaction token (valid for 15 minutes). |
guildId |
string | undefined |
Guild ID if the interaction happened in a server. |
channelId |
string | undefined |
Channel ID. |
user |
raw user data | The user who triggered the interaction. |
member |
GuildMember | undefined |
The guild member who triggered it (if in a guild). |
Common Methods (all types)
Section titled “Common Methods (all types)”| Method | Description |
|---|---|
reply(options) |
Sends the initial response. Accepts content, embeds, components, ephemeral. |
deferReply(options?) |
Acknowledges immediately, showing a loading state. |
editReply(options) |
Edits the initial response (after reply or deferReply). |
deleteReply() |
Deletes the initial response. |
followUp(options) |
Sends a follow-up message. |
Type Guards
Section titled “Type Guards”Use type guard methods on the base Interaction to narrow to the correct subtype:
client.on("interactionCreate", async (interaction) => { if (interaction.isChatInputCommand()) { // CommandInteraction } else if (interaction.isComponent()) { // ComponentInteraction } else if (interaction.isModalSubmit()) { // ModalSubmitInteraction } else if (interaction.isAutocomplete()) { // AutocompleteInteraction }});client.on("interactionCreate", async (interaction) => { if (interaction.isChatInputCommand()) { // CommandInteraction } else if (interaction.isComponent()) { // ComponentInteraction } else if (interaction.isModalSubmit()) { // ModalSubmitInteraction } else if (interaction.isAutocomplete()) { // AutocompleteInteraction }});CommandInteraction — Slash Commands
Section titled “CommandInteraction — Slash Commands”Triggered when a user runs a slash command (/commandname).
Additional Properties
Section titled “Additional Properties”| Property | Type | Description |
|---|---|---|
commandName |
string |
Name of the slash command. |
options |
CommandOptions |
Typed option resolver. |
Reading Options
Section titled “Reading Options”client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand()) return; const { commandName, options } = interaction;
if (commandName === "ban") { const target = options.getUser("target", true); const reason = options.getString("reason") ?? "No reason provided"; // ... }});client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand()) return; const { commandName, options } = interaction;
if (commandName === "ban") { const target = options.getUser("target", true); const reason = options.getString("reason") ?? "No reason provided"; // ... }});CommandOptions Methods
Section titled “CommandOptions Methods”| Method | Returns | Description |
|---|---|---|
getString(name, required?) |
string | null |
Gets a string option. |
getInteger(name, required?) |
number | null |
Gets an integer option. |
getNumber(name, required?) |
number | null |
Gets a float option. |
getBoolean(name, required?) |
boolean | null |
Gets a boolean option. |
getUser(name, required?) |
object | null |
Gets the resolved user data. |
getChannel(name, required?) |
object | null |
Gets the resolved channel data. |
getRole(name, required?) |
object | null |
Gets the resolved role data. |
getAttachment(name, required?) |
object | null |
Gets the attachment data. |
getSubcommand(required?) |
string | null |
Gets the subcommand name. |
getSubcommandGroup(required?) |
string | null |
Gets the subcommand group name. |
When required = true, the method throws a TypeError if the option is missing.
Full Example
Section titled “Full Example”client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "userinfo") { await interaction.deferReply(); const user = interaction.options.getUser("target") ?? interaction.user; const embed = new EmbedBuilder() .setTitle(`User Info: ${(user as any).username}`) .addFields({ name: "ID", value: (user as any).id }); await interaction.editReply({ embeds: [embed] }); }});client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "userinfo") { await interaction.deferReply(); const user = interaction.options.getUser("target") ?? interaction.user; const embed = new EmbedBuilder() .setTitle(`User Info: ${(user as any).username}`) .addFields({ name: "ID", value: (user as any).id }); await interaction.editReply({ embeds: [embed] }); }});ComponentInteraction — Buttons & Select Menus
Section titled “ComponentInteraction — Buttons & Select Menus”Triggered when a user clicks a button or chooses a select menu option.
Additional Properties
Section titled “Additional Properties”| Property | Type | Description |
|---|---|---|
customId |
string |
The customId set on the component. |
componentType |
number |
Component type (2 = Button, 3 = String Select, etc.). |
values |
string[] |
Selected values (only for select menus). |
message |
Message |
The message containing the component. |
Additional Methods
Section titled “Additional Methods”| Method | Description |
|---|---|
update(options) |
Updates the originating message instead of sending a new reply. |
deferUpdate() |
Defers without showing a loading state (for components). |
Example
Section titled “Example”client.on("interactionCreate", async (interaction) => { if (!interaction.isComponent()) return;
if (interaction.customId === "btn_confirm") { await interaction.update({ content: "✅ Confirmed!", components: [] }); }
if (interaction.customId === "select_color") { const colors = interaction.values; // e.g. ["red", "blue"] await interaction.reply({ content: `You selected: ${colors.join(", ")}`, ephemeral: true }); }});client.on("interactionCreate", async (interaction) => { if (!interaction.isComponent()) return;
if (interaction.customId === "btn_confirm") { await interaction.update({ content: "✅ Confirmed!", components: [] }); }
if (interaction.customId === "select_color") { const colors = interaction.values; // e.g. ["red", "blue"] await interaction.reply({ content: `You selected: ${colors.join(", ")}`, ephemeral: true }); }});ModalSubmitInteraction — Modal Submissions
Section titled “ModalSubmitInteraction — Modal Submissions”Triggered when a user submits a modal form.
Additional Properties
Section titled “Additional Properties”| Property | Type | Description |
|---|---|---|
customId |
string |
The customId set on the modal. |
fields |
ModalFields |
Accessor for submitted text input values. |
Reading Field Values
Section titled “Reading Field Values”const value = interaction.fields.getTextInputValue("field_custom_id");const value = interaction.fields.getTextInputValue("field_custom_id");Example
Section titled “Example”client.on("interactionCreate", async (interaction) => { if (!interaction.isModalSubmit()) return;
if (interaction.customId === "feedback_modal") { const subject = interaction.fields.getTextInputValue("subject"); const body = interaction.fields.getTextInputValue("body"); await interaction.reply({ content: `📨 Feedback received!\n**${subject}**\n${body}`, ephemeral: true, }); }});client.on("interactionCreate", async (interaction) => { if (!interaction.isModalSubmit()) return;
if (interaction.customId === "feedback_modal") { const subject = interaction.fields.getTextInputValue("subject"); const body = interaction.fields.getTextInputValue("body"); await interaction.reply({ content: `📨 Feedback received!\n**${subject}**\n${body}`, ephemeral , }); }});AutocompleteInteraction — Live Suggestions
Section titled “AutocompleteInteraction — Live Suggestions”Triggered as a user types in a slash command option with autocomplete: true.
[!IMPORTANT] Autocomplete interactions cannot use
reply(). Userespond()to send choices.
Additional Properties
Section titled “Additional Properties”| Property | Type | Description |
|---|---|---|
commandName |
string |
The slash command name. |
options |
CommandOptions |
Resolved options (use getFocused for the active field). |
respond(choices)
Section titled “respond(choices)”Sends up to 25 autocomplete choice objects:
await interaction.respond([ { name: "Option A", value: "a" }, { name: "Option B", value: "b" },]);await interaction.respond([ { name: "Option A", value: "a" }, { name: "Option B", value: "b" },]);Example
Section titled “Example”client.on("interactionCreate", async (interaction) => { if (!interaction.isAutocomplete()) return;
const focused = interaction.options.getFocused(true); if (focused.name === "tag") { const tags = ["typescript", "javascript", "bun", "discord"]; const filtered = tags.filter(t => t.startsWith(focused.value.toLowerCase())); await interaction.respond(filtered.map(t => ({ name: t, value: t }))); }});client.on("interactionCreate", async (interaction) => { if (!interaction.isAutocomplete()) return;
const focused = interaction.options.getFocused(true); if (focused.name === "tag") { const tags = ["typescript", "javascript", "bun", "discord"]; const filtered = tags.filter(t => t.startsWith(focused.value.toLowerCase())); await interaction.respond(filtered.map(t => ({ name , value: t }))); }});showModal(modal) — Opening a Modal
Section titled “showModal(modal) — Opening a Modal”Any non-autocomplete interaction can show a modal as its initial response:
client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand() || interaction.commandName !== "report") return;
const modal = new ModalBuilder() .setCustomId("report_modal") .setTitle("Report a User") .addComponents( new ActionRowBuilder<TextInputBuilder>().addComponents( new TextInputBuilder() .setCustomId("reason") .setLabel("Reason") .setStyle(TextInputStyle.Paragraph) .setRequired(true), ), );
await interaction.showModal(modal);});client.on("interactionCreate", async (interaction) => { if (!interaction.isChatInputCommand() || interaction.commandName !== "report") return;
const modal = new ModalBuilder() .setCustomId("report_modal") .setTitle("Report a User") .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId("reason") .setLabel("Reason") .setStyle(TextInputStyle.Paragraph) .setRequired(true), ), );
await interaction.showModal(modal);});