Skip to content

Handlers

Handlers are the core of Honocord. They allow you to define both the metadata for Discord (like command names, descriptions, and options) and the logic that should run when an interaction occurs.

Honocord provides specialized handler classes for different types of interactions.

SlashCommandHandler is used for chat input commands. It extends the @discordjs/builders SlashCommandBuilder, so you can use all the familiar methods to define your command’s structure.

import { SlashCommandHandler } from "honocord";
const pingCommand = new SlashCommandHandler()
.setName("ping")
.setDescription("Replies with Pong!")
.addHandler(async (ctx) => {
await ctx.reply("Pong!");
});

Since it extends SlashCommandBuilder, you can add options and subcommands directly.

const userCommand = new SlashCommandHandler()
.setName("userinfo")
.setDescription("Get info about a user")
.addUserOption((option) => option.setName("target").setDescription("The user to lookup").setRequired(true))
.addHandler(async (ctx) => {
const user = ctx.options.getUser("target", true); // if required option, pass true as second argument!
await ctx.reply(`User: ${user.username} (${user.id})`);
});

You can handle autocomplete interactions by adding an autocomplete handler to your command.

const searchCommand = new SlashCommandHandler()
.setName("search")
.setDescription("Search something")
.addStringOption((option) => option.setName("query").setDescription("The search query").setAutocomplete(true))
.addAutocompleteHandler(async (ctx) => {
const focused = ctx.options.getFocused();
const results = ["apple", "banana", "cherry"].filter((s) => s.startsWith(focused.value));
await ctx.respond(results.map((r) => ({ name: r, value: r })));
});

Honocord provides an AutocompleteHelper utility to simplify filtering choices based on user input.

import { AutocompleteHelper } from "honocord";
command.addAutocompleteHandler(async (ctx) => {
const focused = ctx.options.getFocused()!;
const helper = new AutocompleteHelper(focused.value).addChoices(
{ name: "Apple", value: "apple" },
{ name: "Banana", value: "banana" },
{ name: "Cherry", value: "cherry" }
);
// Automatically filters choices that start with the user's input
await ctx.respond(helper.response(["name"]));
});

Use ContextCommandHandler to create User or Message context menu commands.

import { ContextCommandHandler, ContextCommandType } from "honocord";
const userContext = new ContextCommandHandler(ContextCommandType.User).setName("Get ID").addHandler(async (ctx) => {
await ctx.reply(`The user's ID is ${ctx.targetId}`);
});

ComponentHandler manages interactions from buttons and select menus. It uses a prefix-based matching system for custom_ids.

import { ComponentHandler } from "honocord";
import { ComponentType } from "discord-api-types/v10";
// Matches any button with a custom_id starting with "vote:"
const voteButton = new ComponentHandler("vote", ComponentType.Button).addHandler(async (ctx) => {
const [_, candidate] = ctx.customId.split(":");
await ctx.reply(`You voted for ${candidate}!`);
});

You can create handlers for all types of select menus by specifying the correct ComponentType.

const menuHandler = new ComponentHandler("select_role", ComponentType.RoleSelect).addHandler(async (ctx) => {
// ctx is typed specifically for RoleSelect interactions
const roles = ctx.roles;
await ctx.reply(`Selected ${roles.size} roles.`);
});

ModalHandler works similarly to component handlers, matching the custom_id of a submitted modal.

import { ModalHandler } from "honocord";
const feedbackModal = new ModalHandler("feedback_form").addHandler(async (ctx) => {
const feedback = ctx.components.getTextInputValue("feedback_input");
await ctx.reply("Thank you for your feedback!");
});

By default, commands are registered globally. You can restrict a command to specific guilds using .setGuildIds().

const adminCommand = new SlashCommandHandler()
.setName("admin")
.setDescription("Restricted command")
.setGuildIds(["123456789012345678"]) // Only available in this guild
.addHandler(async (ctx) => {
await ctx.reply("Welcome, Admin.");
});

Finally, you must load your handlers into the Honocord instance for them to be processed.

import { Honocord } from "honocord";
const bot = new Honocord();
bot.loadHandlers(pingCommand, userCommand, voteButton, feedbackModal);

Or if you have many handlers, you can load them from an array:

import * as handlers from "./handlers"; // assuming handlers/index.ts exports all (and ONLY) handlers
bot.loadHandlers(...Object.values(handlers));

WebhookEventHandler manages Discord webhook events. Unlike interactions, webhooks are one-way notifications from Discord about events happening in your app.

In standard mode, handlers must return a Response object. This is ideal for non-Cloudflare Workers environments.

import { WebhookEventHandler } from "honocord";
import { ApplicationWebhookEventType } from "discord-api-types/v10";
const messageHandler = new WebhookEventHandler(ApplicationWebhookEventType.LobbyMessageCreate);
messageHandler.addHandler(async (c) => {
const message = c.var.data;
console.log(`New message from ${message.author.username}: ${message.content}`);
// Must return a Response in standard mode
return c.json({ received: true });
});

For Cloudflare Workers, set the second constructor parameter to true. In worker mode:

  • No return value is required from your handler
  • Honocord automatically responds with 200 OK to Discord
  • Processing happens asynchronously via waitUntil, extending the worker’s lifetime
  • fetch() and getApp() methods are disabled (type error + runtime error)
// Enable worker mode with second parameter
const messageHandler = new WebhookEventHandler(
ApplicationWebhookEventType.LobbyMessageCreate,
true // Worker mode
);
messageHandler.addHandler(async (c) => {
const message = c.var.data;
console.log(`Processing message: ${message.content}`);
// No return needed - Honocord handles the response
// Your code runs asynchronously via waitUntil
});
FeatureInteractionsWebhooks
TriggerUser actions (slash commands, buttons)Discord events (entitlements, lobby messages)
ResponseRequired within 3 secondsMust respond within 3 seconds (204 or 200)
Reply Methodinteraction.reply()Use REST API separately
Route/ or /interactions/webhook
SetupInteractions Endpoint URLWebhook Events URL in Discord Developer Portal
CF WorkersAsync via waitUntilAsync via waitUntil (worker mode)

When using WebhookEventHandler with Honocord:

  1. Load handlers using bot.loadHandlers()
  2. The webhook endpoint is automatically mounted at /webhook
  3. Configure your Discord webhook URL to point to https://your-domain.com/webhook
const bot = new Honocord({ isCFWorker: true }); // Enable CF Workers mode
bot.loadHandlers(messageHandler);
// Webhook available at /webhook
// On CF Workers: responds immediately with 200, processes async
// On other platforms: waits for handler to return Response
export default bot.getApp();

Webhook handlers can run independently of Honocord. This is useful for microservices or separate webhook endpoints.

Standard Mode Only: The fetch() and getApp() methods are only available in standard mode (when forWorker is false or omitted).

import { Hono } from "hono";
const handler = new WebhookEventHandler(
ApplicationWebhookEventType.EntitlementCreate
// false or omit for standard mode
);
handler.addHandler(async (c) => {
const entitlement = c.var.data;
console.log(`New entitlement: ${entitlement.id}`);
// Must return Response in standard mode
return c.json({ ok: true });
});
// Use as standalone fetch handler
export default {
fetch: handler.fetch.bind(handler),
};
// Or mount in a Hono app
const app = new Hono();
app.route("/discord", handler.getApp());
export default app;

Discord supports various webhook event types. Common ones include:

  • APPLICATION_AUTHORIZED - App authorized by user
  • ENTITLEMENT_CREATE, ENTITLEMENT_UPDATE, ENTITLEMENT_DELETE - Monetization events
  • LOBBY_MESSAGE_CREATE, LOBBY_MESSAGE_UPDATE, LOBBY_MESSAGE_DELETE - Lobby messages
  • GAME_DIRECT_MESSAGE_CREATE, GAME_DIRECT_MESSAGE_UPDATE, GAME_DIRECT_MESSAGE_DELETE - Game DMs

Refer to the Discord Webhook Events documentation for the complete list.