honocord 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,401 +1,433 @@
1
- # HonoCord
1
+ # Features
2
2
 
3
- A Discord interaction handler library for [Hono](https://hono.dev/), designed to work seamlessly with Cloudflare Workers and other edge runtimes.
3
+ Honocord brings together the power of Discord's Interaction API and Hono's lightweight framework with a focus on developer experience and edge computing.
4
4
 
5
- ## Features
5
+ _Yes, AI helped me build this - with the focus on **helped**._
6
6
 
7
- - 🔥 Built on top of Hono for maximum performance
8
- - ⚡ Perfect for Cloudflare Workers and edge runtimes
9
- - 🎯 Type-safe interaction handlers
10
- - 🔐 Built-in request verification
11
- - 🎨 Support for all Discord interaction types:
12
- - Slash commands with autocomplete
13
- - User and Message context menu commands
14
- - Message components (buttons, select menus)
15
- - Modal submissions
16
- - 🗂️ Prefix-based routing for components and modals
17
- - 📦 Minimal dependencies using `@discordjs/core` and `@discordjs/builders`
7
+ ## 🚀 Core Features
18
8
 
19
- <small>Yes, AI helped me build this. With the focus on **helped**.</small>
9
+ ---
20
10
 
21
- ## Installation
11
+ ### Type-Safe Development
22
12
 
23
- ```bash
24
- pnpm add honocord@latest
13
+ Full TypeScript support throughout the entire stack:
14
+
15
+ - **End-to-End Types** - From handlers to Discord API responses
16
+ - **Autocomplete & IntelliSense** - IDE support for all methods and properties
17
+ - **Type Guards** - Runtime type checking with TypeScript inference
18
+ - **Custom Environment Types** - Extend with your own bindings and variables
19
+
20
+ ```typescript
21
+ // TypeScript knows the exact type
22
+ const name = interaction.options.getString("name", true); // string
23
+ const count = interaction.options.getInteger("count"); // number | null
24
+
25
+ if (interaction.inGuild()) {
26
+ const guildId = interaction.guildId; // TypeScript knows this exists
27
+ }
25
28
  ```
26
29
 
27
- The most important stuff from the dependencies is already exported through honocord itselfy but if you need more from `discord-api-types` for example, you should install it yourself as well.
30
+ ---
31
+
32
+ ### Handler-Based Architecture
28
33
 
29
- ## Quick Start
34
+ Clean, modular code organization:
30
35
 
31
36
  ```typescript
32
- import { Honocord, SlashCommandHandler } from "honocord";
37
+ // Define handlers separately
38
+ const pingCommand = new SlashCommandHandler()
39
+ .setName("ping")
40
+ .setDescription("Pong!")
41
+ .addHandler(async (interaction) => {
42
+ await interaction.reply("🏓 Pong!");
43
+ });
33
44
 
34
- const bot = new Honocord();
45
+ // Load them together
46
+ bot.loadHandlers(pingCommand, greetCommand, buttonHandler);
47
+ ```
35
48
 
36
- // Define a slash command
37
- const pingCommand = new SlashCommandHandler().setName("ping").setDescription("Replies with Pong!");
49
+ **Benefits:**
38
50
 
39
- // Add handler to the command
40
- pingCommand.addHandler(async (interaction) => {
41
- await interaction.reply("Pong! 🏓");
42
- });
51
+ - **Separation of Concerns** - Each handler is self-contained
52
+ - **Easy Testing** - Test handlers in isolation
53
+ - **Reusability** - Share handlers across projects
54
+ - **Hot Reloading** - Change handlers without restarting (in dev mode)
43
55
 
44
- // Register handlers
45
- bot.loadHandlers(pingCommand);
56
+ ---
46
57
 
47
- // Export the app
48
- export default bot.getApp();
58
+ ## 💬 Interaction Support
49
59
 
50
- // Or use with your own Hono app
51
- import { Hono } from "hono";
60
+ ### Slash Commands
52
61
 
53
- const app = new Hono();
62
+ Full-featured slash command support:
63
+
64
+ - **Options & Validation** - String, integer, boolean, user, role, channel, and more
65
+ - **Subcommands & Groups** - Organize complex command structures
66
+ - **Required & Optional** - Fine-grained control over user input
67
+ - **Autocomplete** - Real-time suggestions as users type
68
+
69
+ ```typescript
70
+ const searchCommand = new SlashCommandHandler()
71
+ .setName("search")
72
+ .setDescription("Search for items")
73
+ .addStringOption((option) =>
74
+ option.setName("query").setDescription("What to search for").setAutocomplete(true).setRequired(true)
75
+ )
76
+ .addHandler(handleSearch)
77
+ .addAutocompleteHandler(handleAutocomplete);
54
78
  ```
55
79
 
56
- ## Usage with and without Cloudflare Workers
80
+ ---
57
81
 
58
- Hono, well rather CF Workers, have a small issue with responding to interactions.
82
+ ### Context Menus
59
83
 
60
- When using HonoCord in a Cloudflare Worker environment, you should ensure that your environment variable `IS_CF_WORKER` is set to `"true"` **or** you pass this information to HonoCord in the constructor:
84
+ Right-click context menu commands:
85
+
86
+ - **User Commands** - Actions on users (ban, view profile, etc.)
87
+ - **Message Commands** - Actions on messages (report, translate, etc.)
61
88
 
62
89
  ```typescript
63
- const bot = new Honocord({ isCFWorker: true }); // true indicates CF Worker environment
90
+ const reportUser = new ContextCommandHandler()
91
+ .setName("Report User")
92
+ .setType(ApplicationCommandType.User)
93
+ .addHandler(async (interaction) => {
94
+ const user = interaction.targetUser;
95
+ // Handle report...
96
+ });
64
97
  ```
65
98
 
66
- <details>
67
- <summary>Why that is needed?</summary>
99
+ ---
68
100
 
69
- On Cloudflare Workers, we need to make use of `c.executionContext.waitUntil()` to handle asynchronous tasks properly without blocking the response. By checking for the `IS_CF_WORKER` environment variable, HonoCord can determine if it's running in a Cloudflare Worker environment and adjust its behavior accordingly.
101
+ ### Message Components
70
102
 
71
- This approach allows HonoCord to maintain compatibility with both Cloudflare Workers and other environments, ensuring that interactions are handled correctly regardless of where the code is executed.
103
+ Interactive buttons and select menus:
72
104
 
73
- </details>
105
+ - **Buttons** - Primary, secondary, success, danger, link styles
106
+ - **Select Menus** - String, user, role, channel, mentionable options
107
+ - **Prefix Matching** - One handler for multiple related components
108
+ - **Parameter Passing** - Pass data through custom IDs
74
109
 
75
- > [!IMPORTANT]
76
- > This readme assumes you are using Cloudflare Workers.
77
- > Most stuff is the same for other environments, but keep in mind the `IS_CF_WORKER` variable and the way you export and use your Hono app.
110
+ ```typescript
111
+ // Create a button
112
+ new ButtonBuilder().setCustomId("confirm/action?1234567890").setLabel("Confirm").setStyle(ButtonStyle.Success);
113
+
114
+ // Handle with prefix matching
115
+ const confirmHandler = new ComponentHandler("confirm").addHandler(async (interaction) => {
116
+ const { firstParam } = parseCustomId(interaction.custom_id);
117
+ // Use param...
118
+ });
119
+ ```
120
+
121
+ ---
122
+
123
+ ### Modals (Forms)
78
124
 
79
- ## Philosophy - How It Works
125
+ Full modal/form support:
80
126
 
81
- HonoCord leverages Hono's lightweight and fast request handling capabilities to process Discord interactions efficiently. It verifies incoming requests using Discord's public key, ensuring security and authenticity.
127
+ - **Text Inputs** - Short and paragraph styles
128
+ - **Validation** - Min/max length, required fields
129
+ - **Field Access** - Type-safe field resolution
130
+ - **Flexible Triggers** - Show from commands or components
82
131
 
83
- To handle interactions, you define various handler types (slash commands, context commands, components, modals) and register them with the `Honocord` instance. Each handler processes its respective interaction type.
132
+ ```typescript
133
+ await interaction.showModal(
134
+ new ModalBuilder({
135
+ custom_id: "feedback",
136
+ title: "Send Feedback",
137
+ }).addLabelComponents(
138
+ new LabelBuilder()
139
+ .setLabel("Your Feedback")
140
+ .setTextInputComponent((input) => input.setCustomId("message").setStyle(TextInputStyle.Paragraph).setRequired(true))
141
+ )
142
+ );
143
+ ```
84
144
 
85
- ### Custom IDs
145
+ ---
86
146
 
87
- Custom IDs for components and modals use a **prefix-based routing system**, allowing you to easily manage multiple interactions with shared logic.
147
+ ## 🔧 Developer Experience
88
148
 
89
- Custom IDs follow the pattern: `prefix/component/path?param1/param2`
149
+ ### Minimal Boilerplate
90
150
 
91
- As you can see, it is basically split into two parts: a **path** and **parameters**.
92
- Both parts are separated by a `?`, and each part is further divided by `/` (every path-segment and param). However, the `prefix` defines which handler will process the interaction.
151
+ _Well, I tried at least..._
93
152
 
94
- Use the `parseCustomId` utility to parse custom IDs:
153
+ Get started in seconds:
95
154
 
96
155
  ```typescript
97
- import { parseCustomId } from "honocord";
156
+ import { Honocord, SlashCommandHandler } from "honocord";
98
157
 
99
- // Example: "approve/user/request?user123/action456"
100
- const parsed = parseCustomId("approve/user/request?user123/action456");
158
+ const bot = new Honocord();
101
159
 
102
- console.log(parsed.prefix); // "approve"
103
- console.log(parsed.component); // "user"
104
- console.log(parsed.lastPathItem); // "request"
105
- console.log(parsed.compPath); // ["approve", "user", "request"]
106
- console.log(parsed.params); // ["user123", "action456"]
107
- console.log(parsed.firstParam); // "user123"
108
- console.log(parsed.lastParam); // "action456"
160
+ bot.loadHandlers(
161
+ new SlashCommandHandler()
162
+ .setName("ping")
163
+ .setDescription("Pong!")
164
+ .addHandler(async (i) => await i.reply("Pong!"))
165
+ );
109
166
 
110
- // Get only the prefix
111
- const prefix = parseCustomId("approve/user/request?user123", true); // "approve"
167
+ export default bot.getApp();
112
168
  ```
113
169
 
114
- ## Handlers
170
+ ---
115
171
 
116
- ### 1. Slash Command Handler
172
+ ### Built-in Utilities
117
173
 
118
- Slash commands are the primary way users interact with your bot. They support autocomplete for option values.
174
+ Helpful utilities included:
175
+
176
+ **Custom ID Parser:**
119
177
 
120
178
  ```typescript
121
- import { SlashCommandHandler } from "honocord";
179
+ const { prefix, component, params } = parseCustomId("action/button?user123");
180
+ ```
122
181
 
123
- const searchCommand = new SlashCommandHandler()
124
- .setName("search")
125
- .setDescription("Search for something")
126
- .addStringOption((option) =>
127
- option.setName("query").setDescription("What to search for").setRequired(true).setAutocomplete(true)
128
- )
129
- .addHandler(async (interaction) => {
130
- const query = interaction.options.getString("query", true);
131
- await interaction.reply(`Searching for: ${query}`);
132
- })
133
- .addAutocompleteHandler(async (interaction) => {
134
- const focusedValue = interaction.options.getFocused();
135
- const choices = ["apple", "banana", "cherry", "dragon fruit", "elderberry"]
136
- .filter((choice) => choice.startsWith(focusedValue.toLowerCase()))
137
- .slice(0, 25); // Discord limits to 25 choices
138
-
139
- await interaction.respond(choices.map((choice) => ({ name: choice, value: choice })));
140
- });
182
+ **Autocomplete Helper:**
141
183
 
142
- bot.loadHandlers(searchCommand);
184
+ ```typescript
185
+ const autocompleteResponse = new AutocompleteHelper("query", interaction.user)
186
+ .addChoices({ name: "Option 1", value: "opt1" })
187
+ .response(); // Automatically filters
188
+ await interaction.respond(autocompleteResponse); // Sends filtered choices
143
189
  ```
144
190
 
145
- ### 2. Context Command Handler
191
+ **Color Constants:**
192
+
193
+ ```typescript
194
+ import { Colors } from "honocord";
195
+
196
+ const embed = new EmbedBuilder().setColor(Colors.Blue);
197
+ ```
146
198
 
147
- Context commands appear in the right-click menu for users or messages.
199
+ **Command Registration:**
148
200
 
149
201
  ```typescript
150
- import { ContextCommandHandler } from "honocord";
151
- import { ApplicationCommandType } from "discord-api-types/v10";
202
+ await registerCommands(token, appId, ...handlers);
203
+ // You can also pass a single array
204
+ await registerCommands(token, appId, handlers);
205
+ ```
152
206
 
153
- // User context command
154
- const userInfoCommand = new ContextCommandHandler().setName("User Info").setType(ApplicationCommandType.User);
207
+ ---
155
208
 
156
- userInfoCommand.addHandler(async (interaction) => {
157
- const user = interaction.targetUser;
158
- await interaction.reply(`User: ${user.username} (${user.id})`);
159
- });
209
+ ### Flexible Integration
160
210
 
161
- // Message context command
162
- const translateCommand = new ContextCommandHandler().setName("Translate").setType(ApplicationCommandType.Message);
211
+ Use Honocord your way:
163
212
 
164
- translateCommand.addHandler(async (interaction) => {
165
- const message = interaction.targetMessage;
166
- await interaction.reply(`Translating: "${message.content}"`);
167
- });
213
+ **Standalone App:**
168
214
 
169
- bot.loadHandlers(userInfoCommand, translateCommand);
215
+ ```typescript
216
+ const bot = new Honocord();
217
+ export default bot.getApp();
170
218
  ```
171
219
 
172
- ### 3. Component Handler
173
-
174
- Component handlers handle interactions from buttons, select menus, and other message components. They use a **prefix-based routing system**.
220
+ **Custom Hono Integration:**
175
221
 
176
222
  ```typescript
177
- import { ComponentHandler } from "honocord";
178
- import { ButtonBuilder, ButtonStyle, ActionRowBuilder } from "@discordjs/builders";
179
-
180
- // Create a button with a custom_id using a prefix
181
- const button = new ButtonBuilder()
182
- .setCustomId("approve/user123") // prefix: "approve"
183
- .setLabel("Approve")
184
- .setStyle(ButtonStyle.Success);
185
-
186
- // Handler for all components with the "approve" prefix
187
- const approveHandler = new ComponentHandler("approve", async (interaction) => {
188
- // Parse the custom_id to get parameters
189
- const { params } = parseCustomId(interaction.customId);
190
- const userId = params[0]; // "user123"
191
-
192
- await interaction.reply(`Approved user: ${userId}`);
193
- });
223
+ const app = new Hono();
224
+ app.get("/", (c) => c.text("Hello"));
225
+ app.post("/interactions", bot.handle);
226
+ ```
227
+
228
+ **With Middleware:**
194
229
 
195
- bot.loadHandlers(approveHandler);
230
+ ```typescript
231
+ app.use("*", logger());
232
+ app.use("*", cors());
233
+ app.post("/interactions", bot.handle);
196
234
  ```
197
235
 
198
- ### 4. Modal Handler
236
+ ---
199
237
 
200
- Modal handlers process form submissions. Like components, they use prefix-based routing.
238
+ ## 🔐 Security
201
239
 
202
- ```typescript
203
- import { ModalHandler } from "honocord";
204
- import { ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder } from "@discordjs/builders";
205
-
206
- // Create a modal
207
- const modal = new ModalBuilder()
208
- .setCustomId("feedback/feature") // prefix: "feedback"
209
- .setTitle("Feature Feedback");
210
-
211
- // ActionRows in modals are deprecated and should not be used because Honocord doesn't process them!
212
- const input = new LabelBuilder()
213
- .setLabel("What would you like to see?")
214
- .setTextInputComponent(
215
- new TextInputBuilder()
216
- .setCustomId("feedback_text")
217
- .setStyle(TextInputStyle.Paragraph)
218
- .setRequired(true)
219
- );
220
-
221
- modal.addLabelComponents(input);
222
-
223
- // Handler for all modals with the "feedback" prefix
224
- const feedbackHandler = new ModalHandler("feedback", async (interaction) => {
225
- const { component } = parseCustomId(interaction.customId);
226
- const feedbackText = interaction.fields.getTextInputValue("feedback_text");
227
-
228
- await interaction.reply({
229
- content: `Thanks for your ${component} feedback: "${feedbackText}"`,
230
- ephemeral: true,
231
- });
232
- });
240
+ ### Automatic Signature Verification
233
241
 
234
- bot.loadHandlers(feedbackHandler);
235
- ```
242
+ Every request is automatically verified by the same logic [discord-interactions](https://github.com/discord/discord-interactions-js) has (without the extra dependency).
243
+
244
+ ---
236
245
 
237
- ## Complete Example
246
+ ### Environment-Based Configuration
238
247
 
239
- Please refer to the [Examples](https://github.com/The-LukeZ/honocord-examples) repository for complete working examples, including deployment to Cloudflare Workers.
248
+ Keep secrets secure:
240
249
 
241
- ## Environment Variables
250
+ ```typescript
251
+ // Access through context
252
+ const token = interaction.context.env.DISCORD_TOKEN;
242
253
 
243
- ```env
244
- DISCORD_APPLICATION_ID=your_application_id_here # only required for command registration
245
- DISCORD_TOKEN=your_bot_token_here # always required
246
- DISCORD_PUBLIC_KEY=your_public_key_here # always required
247
- IS_CF_WORKER=true # only required if using Cloudflare Workers
254
+ // Never hardcoded
255
+ const bot = new Honocord(); // Uses env vars automatically
248
256
  ```
249
257
 
250
- The `DISCORD_PUBLIC_KEY` is required for request verification and should be available in your environment (e.g., `c.env.DISCORD_PUBLIC_KEY` in Cloudflare Workers).
258
+ ---
251
259
 
252
- ## TypeScript Support
260
+ ## Performance
253
261
 
254
- HonoCord is written in TypeScript and provides full type safety.
262
+ ### Efficient Routing
255
263
 
256
- ### Custom Environment Types
264
+ Optimized handler lookup:
257
265
 
258
- ```typescript
259
- // src/types.ts
260
- import type { BaseHonocordEnv, BaseInteractionContext } from "honocord";
261
-
262
- type MyEnv = {
263
- MY_VARIABLE: string;
264
- };
265
-
266
- export type HonoEnv = BaseHonocordEnv<MyEnv>;
267
- export type MyContext = BaseInteractionContext<MyEnv>;
268
-
269
- // src/index.ts
270
- import type { BaseHonocordEnv, BaseInteractionContext } from "honocord";
271
- import type { HonoEnv, MyContext } from "./types";
272
-
273
- // Use in your handlers
274
- const command = new SlashCommandHandler()
275
- .setName("data")
276
- .setDescription("Fetch data")
277
- .addHandler(async (interaction: MyContext) => {
278
- const myVariable = interaction.env.MY_VARIABLE; // Fully typed!
279
- // ...
280
- });
281
- ```
266
+ - **Commands, Components and Modals**: O(1) hash map lookup by name/custom_id-prefix
267
+ - **Minimal Overhead**: Direct API access, no unnecessary layers (except the discordjs API wrapper and REST client)
282
268
 
283
- ## Registering Commands
269
+ ---
284
270
 
285
- You can register your commands using the `registerCommands()` utility function.
271
+ ### Deferred Responses
272
+
273
+ Handle long-running operations:
286
274
 
287
275
  ```typescript
288
- // src/handlers/index.ts
289
- export * from "./pingCommand";
290
- export * from "./approveHandler";
291
- // ...export other handlers
276
+ await interaction.deferReply();
292
277
 
293
- // src/register.ts
294
- import { registerCommands } from "honocord";
295
- import * as handlers from "./handlers/index";
278
+ // Do expensive work
279
+ await processLargeDataset();
296
280
 
297
- await registerCommands(process.env.DISCORD_TOKEN!, process.env.DISCORD_APPLICATION_ID!, ...Object.values(handlers));
281
+ // Update when ready
282
+ await interaction.editReply("Complete!");
298
283
  ```
299
284
 
300
- You should add the script to your package.json:
285
+ ---
301
286
 
302
- ```json
303
- {
304
- "scripts": {
305
- // tsx is recommended for running TypeScript files directly, but you can use any method you prefer, even node itself if you use a JS file.
306
- "register": "tsx --env-file=.env src/register.ts"
307
- }
308
- }
287
+ ### Ephemeral Messages
288
+
289
+ Reduce server clutter:
290
+
291
+ ```typescript
292
+ // Only visible to user
293
+ await interaction.reply("Secret info", true);
309
294
  ```
310
295
 
311
- ## API Reference
296
+ ---
297
+
298
+ ## 🎨 Rich Content
312
299
 
313
- ### `Honocord`
300
+ ### Discord.js Builders
314
301
 
315
- Main class for handling Discord interactions.
302
+ Re-exports most important builders like EmbedBuilder and Components V2 Builders.
316
303
 
317
- **Constructor:**
304
+ ```typescript
305
+ import { EmbedBuilder, ActionRowBuilder, ButtonBuilder } from "honocord";
306
+
307
+ const embed = new EmbedBuilder().setTitle("Hello!").setColor(Colors.Blue).setDescription("Welcome to the server");
318
308
 
319
- - `new Honocord(options?: HonocordOptions)` - Creates a new instance
309
+ const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
310
+ new ButtonBuilder().setCustomId("welcome").setLabel("Get Started").setStyle(1) // Primary Style
311
+ );
320
312
 
321
- **Methods:**
313
+ await interaction.reply({
314
+ embeds: [embed],
315
+ components: [row],
316
+ });
322
317
 
323
- - `loadHandlers(handlers: Handler[])` - Registers interaction handlers
324
- - `handle(c: Context)` - Hono handler for processing interactions
325
- - `getApp()` - Returns a pre-configured Hono app instance
326
- - Route: `POST /interactions` - Handles Discord interaction requests
327
- - Route: `GET *` - Displays a generic "Is Running" text.
318
+ // Or with Components V2
319
+ import { ContainerBuilder, TextDisplayBuilder, Colors } from "honocord";
328
320
 
329
- ### `SlashCommandHandler`
321
+ const container = new ContainerBuilder().setAccentColor(Colors.Green).addTextDisplayComponents(
322
+ new TextDisplayBuilder().setContent("# Hello, world!");
323
+ )
330
324
 
331
- Extends `SlashCommandBuilder` from `@discordjs/builders`.
325
+ await interaction.reply({
326
+ components: [container],
327
+ });
328
+ ```
332
329
 
333
- **Methods:**
330
+ You don't need to call `.toJSON()` manually - Honocord does it for you. Discord.js inspired.
334
331
 
335
- - `addHandler(handler: (interaction: ChatInputCommandInteraction) => Promise<void> | void)` - Adds the command execution handler
336
- - `addAutocompleteHandler(handler: (interaction: AutocompleteInteraction) => Promise<void> | void)` - Adds an optional autocomplete handler
332
+ ---
337
333
 
338
- ### `ContextCommandHandler`
334
+ ## 🌍 Deployment Options
339
335
 
340
- Extends `ContextMenuCommandBuilder` from `@discordjs/builders`.
336
+ ### Cloudflare Workers
341
337
 
342
- **Constructor:**
338
+ Deploy globally in seconds:
343
339
 
344
- ```ts
345
- new ContextCommandHandler<
346
- T extends ContextCommandType = ContextCommandType,
347
- InteractionData = T extends ContextCommandType.User ? UserContextInteraction : MessageContextInteraction,
348
- > ()
340
+ ```bash
341
+ wrangler deploy
349
342
  ```
350
343
 
351
- `ContextCommandType` is an enum with values `User` and `Message`; Derived from ApplicationCommandType, but narrowed down to the two context command types.
344
+ Cloudflare Workers has some unique configurations that Honocord needs to function. See the [Cloudflare Workers Deployment Guide](/wiki/Deployment-Guide#cloudflare-workers) for details.
345
+
346
+ ---
347
+
348
+ ### Traditional Servers
349
+
350
+ Works anywhere Node.js runs:
352
351
 
353
- **Methods:**
352
+ ```bash
353
+ node index.js
354
+ # or
355
+ bun run index.ts
356
+ ```
354
357
 
355
- - `addHandler(handler: (interaction: InteractionData) => Promise<void> | void)` - Adds the command execution handler
358
+ ---
356
359
 
357
- ### `ComponentHandler`
360
+ ### Docker
358
361
 
359
- Handler for message components.
362
+ Containerize your bot:
360
363
 
361
- **Constructor:**
364
+ ```dockerfile
365
+ FROM oven/bun:1
366
+ COPY . .
367
+ RUN bun install
368
+ CMD ["bun", "run", "index.ts"]
369
+ ```
362
370
 
363
- - `new ComponentHandler<T extends MessageComponentType = MessageComponentType>(prefix: string, handler?: Function)` - Creates a handler for components with the given prefix
371
+ ---
364
372
 
365
- `MessageComponentType` is a union of `ComponentType` values that represent message components (e.g., Button, StringSelectMenu).
373
+ ## 📦 Lightweight
366
374
 
367
- **Methods:**
375
+ ### Minimal Dependencies
368
376
 
369
- - `addHandler(handler: (interaction: MessageComponentInteraction<T>) => Promise<void> | void)` - Adds or replaces the component handler function
377
+ Honocord only requires:
370
378
 
371
- ### `ModalHandler`
379
+ - `hono` - Web framework
380
+ - `discord-api-types` - Type definitions
381
+ - `@discordjs/core` - API wrapper
382
+ - `@discordjs/rest` - HTTP REST client
383
+ - `@discordjs/builders` - Builders
372
384
 
373
- Handler for modal submissions.
385
+ ---
374
386
 
375
- **Constructor:**
387
+ ### TypeScript First
376
388
 
377
- - `new ModalHandler(prefix: string, handler?: Function)` - Creates a handler for modals with the given prefix
389
+ Written in TypeScript, designed for TypeScript:
378
390
 
379
- **Methods:**
391
+ - Source maps included
392
+ - Full type inference
393
+ - JSDoc comments
394
+ - Declaration files
380
395
 
381
- - `addHandler(handler: (interaction: ModalInteraction) => Promise<void> | void)` - Adds or replaces the modal submit handler function
396
+ ---
382
397
 
383
- ### `parseCustomId`
398
+ ### Extensible
384
399
 
385
- Utility function for parsing custom IDs.
400
+ Extend with custom types:
386
401
 
387
402
  ```typescript
388
- // Get full parsing details
389
- parseCustomId(customId: string): ParsedCustomId
403
+ interface MyEnv {
404
+ DATABASE: D1Database;
405
+ CACHE: KVNamespace;
406
+ }
407
+
408
+ type MyContext = BaseInteractionContext<MyEnv>;
390
409
 
391
- // Get only the prefix
392
- parseCustomId(customId: string, onlyPrefix: true): string
410
+ // Now fully typed in handlers
411
+ new SlashCommandHandler<MyContext>().addHandler(async (interaction) => {
412
+ const db = interaction.context.env.DATABASE;
413
+ const cache = interaction.context.env.CACHE;
414
+ });
393
415
  ```
394
416
 
395
- ## License
417
+ ---
396
418
 
397
- MIT
419
+ ### Rate Limit Handling
398
420
 
399
- ## Contributing
421
+ Respects Discord rate limits automatically via `@discordjs/rest`.
400
422
 
401
- Contributions are welcome! Please feel free to submit a Pull Request.
423
+ ---
424
+
425
+ ### Monitoring
426
+
427
+ Built-in debug logging:
428
+
429
+ ```typescript
430
+ const bot = new Honocord({
431
+ debugRest: true, // Log all REST requests
432
+ });
433
+ ```