vimcord 1.0.59 → 2.0.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
@@ -9,7 +9,7 @@
9
9
  ╚═══╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝
10
10
  ```
11
11
 
12
- **vhem-cord** the Discord.js framework that actually respects your time
12
+ **vhem-cord** - a small Discord.js framework for typed modules, command dispatching, and reusable UX tools.
13
13
 
14
14
  [![npm version](https://img.shields.io/npm/v/vimcord?color=%235865F2&label=vimcord&logo=npm&style=flat-square)](https://www.npmjs.com/package/vimcord)
15
15
  [![npm downloads](https://img.shields.io/npm/dm/vimcord?color=%2357F287&label=downloads&style=flat-square)](https://www.npmjs.com/package/vimcord)
@@ -25,263 +25,335 @@
25
25
 
26
26
  ## What's Vimcord?
27
27
 
28
- **Vimcord** (pronounced _vhem-cord_) is a lightweight, opinionated framework for **Discord.js**. Built for developers who want to go from an idea to a working command without fighting boilerplate.
28
+ **Vimcord** wraps Discord.js with a focused module layer, typed command contexts, shared permission checks, global hooks, and UX helpers for embeds, prompts, modals, components, and pagination.
29
29
 
30
- Think of it as Discord.js with the sharp edges sanded off full TypeScript inference, automatic error boundaries, and utilities that actually make sense.
31
-
32
- > "I just wanted to build a bot, not write a command handler for the 47th time." — _You, probably_
30
+ It does not hide Discord.js. You still use Discord.js builders, intents, events, permissions, and interactions directly where that is the right tool.
33
31
 
34
32
  ---
35
33
 
36
34
  ## Installation
37
35
 
38
36
  ```bash
39
- npm install vimcord discord.js
40
- # or
41
37
  pnpm add vimcord discord.js
42
- # or
43
- yarn add vimcord discord.js
44
38
  ```
45
39
 
46
- **Peer dependencies** (optional but recommended):
40
+ Optional plugins:
47
41
 
48
42
  ```bash
49
- npm install mongoose # Only if using MongoDB
43
+ pnpm add @vimcord/plugin-dotenv
44
+ pnpm add @vimcord/plugin-mongoose
50
45
  ```
51
46
 
52
47
  ---
53
48
 
54
49
  ## Quick Start
55
50
 
56
- ### The Absolute Minimum
51
+ ### Minimum Client
57
52
 
58
53
  ```ts
59
- import { createClient, GatewayIntentBits } from "vimcord";
54
+ import { GatewayIntentBits } from "discord.js";
55
+ import { Vimcord } from "vimcord";
60
56
 
61
- const client = createClient({ intents: [GatewayIntentBits.Guilds] }, { useDefaultSlashCommandHandler: true });
57
+ const client = new Vimcord({
58
+ client: {
59
+ intents: [GatewayIntentBits.Guilds]
60
+ }
61
+ });
62
62
 
63
- client.start();
63
+ await client.login();
64
64
  ```
65
65
 
66
- ### The "I Actually Want Features" Setup
66
+ `login()` uses `TOKEN`, or `TOKEN_DEV` when `client.$devMode` is true.
67
+
68
+ ### Modules And Command Dispatch
67
69
 
68
70
  ```ts
69
71
  import { GatewayIntentBits } from "discord.js";
70
- import { createClient, MongoDatabase } from "vimcord";
72
+ import { Vimcord } from "vimcord";
73
+ import { DotEnvPlugin } from "@vimcord/plugin-dotenv";
71
74
 
72
- const client = createClient(
73
- {
75
+ const client = new Vimcord({
76
+ client: {
74
77
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent]
75
78
  },
76
- {
77
- // Auto-load modules from directories
78
- importModules: {
79
- events: "./events",
80
- slashCommands: "./commands/slash",
81
- prefixCommands: "./commands/prefix",
82
- contextCommands: "./commands/context"
79
+ globals: {
80
+ app: {
81
+ name: "My Bot"
83
82
  },
83
+ staff: {
84
+ ownerId: "123456789012345678",
85
+ superUsers: ["234567890123456789"]
86
+ }
87
+ },
88
+ connectionRefresh: {
89
+ interval: 60_000,
90
+ maxFailures: 2
91
+ },
92
+ verbose: true
93
+ });
84
94
 
85
- // Built-in handlers for each command type
86
- useDefaultSlashCommandHandler: true,
87
- useDefaultPrefixCommandHandler: true,
88
- useDefaultContextCommandHandler: true,
95
+ client.use(new DotEnvPlugin());
89
96
 
90
- // Catch and log unhandled errors
91
- useGlobalErrorHandlers: true
92
- }
93
- );
97
+ await client.modules.load({
98
+ slashCommands: { dir: "./src/commands/slash", suffix: ".slash" },
99
+ prefixCommands: { dir: "./src/commands/prefix", suffix: ".prefix" },
100
+ events: { dir: "./src/events", suffix: ".event" }
101
+ });
102
+
103
+ client.on("interactionCreate", interaction => {
104
+ void client.modules.commands.dispatchInteraction(interaction);
105
+ });
94
106
 
95
- client.start(async () => {
96
- // Connect to MongoDB before login
97
- await client.useDatabase(new MongoDatabase(client));
107
+ client.on("messageCreate", message => {
108
+ if (message.author.bot) return;
109
+ void client.modules.commands.dispatchMessage(message, ["!"]);
98
110
  });
111
+
112
+ await client.login();
113
+ await client.modules.commands.push();
99
114
  ```
100
115
 
101
116
  ---
102
117
 
103
118
  ## Features
104
119
 
105
- ### Command Management That Doesn't Suck
106
-
107
- **Slash commands** with subcommand routing:
120
+ ### Slash Commands
108
121
 
109
122
  ```ts
110
- import { SlashCommandBuilder } from "vimcord";
123
+ import { PermissionFlagsBits, SlashCommandBuilder } from "discord.js";
124
+ import { SlashCommandModule } from "vimcord";
111
125
 
112
- export default new SlashCommandBuilder({
126
+ export default new SlashCommandModule({
113
127
  builder: new SlashCommandBuilder()
114
128
  .setName("manage")
115
129
  .setDescription("Server management")
116
- .addSubcommand(sub => sub.setName("ban").setDescription("Ban a user")),
130
+ .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
131
+ .addSubcommand(subcommand => subcommand.setName("ban").setDescription("Ban a user")),
117
132
 
118
- // Route subcommands automatically
119
133
  routes: [
120
134
  {
121
- name: "ban",
122
- handler: async (client, interaction) => {
123
- // Handle /manage ban
135
+ path: "ban",
136
+ deferReply: { flags: "Ephemeral" },
137
+ async handler({ interaction }) {
138
+ await interaction.editReply("Ban flow started.");
124
139
  }
125
140
  }
126
141
  ],
127
142
 
128
- // Permission inference — Vimcord validates before executing
129
143
  permissions: {
130
- user: [PermissionFlagsBits.BanMembers],
131
- bot: [PermissionFlagsBits.BanMembers]
144
+ client: [PermissionFlagsBits.BanMembers]
132
145
  }
133
146
  });
134
147
  ```
135
148
 
136
- **Prefix commands** with the same DX:
149
+ ### Prefix Commands
137
150
 
138
151
  ```ts
139
- import { PrefixCommandBuilder } from "vimcord";
152
+ import { PrefixCommandModule } from "vimcord";
140
153
 
141
- export default new PrefixCommandBuilder({
154
+ export default new PrefixCommandModule({
142
155
  name: "ping",
143
156
  aliases: ["p"],
144
- execute: async (client, message, args) => {
145
- await message.reply("Pong!");
157
+ async execute({ message, messageContent, splitContent }) {
158
+ const args = splitContent({ lowercase: true });
159
+ await message.reply(`Pong. You sent ${args.length} arg${args.length === 1 ? "" : "s"}.`);
146
160
  }
147
161
  });
148
162
  ```
149
163
 
150
- ### BetterEmbed: Stop Writing Boilerplate
164
+ ### Event Modules
151
165
 
152
166
  ```ts
153
- import { BetterEmbed } from "vimcord";
167
+ import { Events } from "discord.js";
168
+ import { EventModule } from "vimcord";
169
+
170
+ export default new EventModule({
171
+ name: "logMessages",
172
+ event: Events.MessageCreate,
173
+ async execute({ args: [message] }) {
174
+ if (message.author.bot) return;
175
+ console.log(`${message.author.tag}: ${message.content}`);
176
+ }
177
+ });
178
+ ```
179
+
180
+ ### Global Command Hooks
181
+
182
+ ```ts
183
+ import { defineGlobalCommandHooks } from "vimcord";
184
+
185
+ defineGlobalCommandHooks({
186
+ slash: {
187
+ async onError({ interaction, error }) {
188
+ const content = `Something went wrong: ${error?.message ?? "Unknown error"}`;
189
+
190
+ if (interaction.replied || interaction.deferred) {
191
+ await interaction.followUp({ content, flags: "Ephemeral" });
192
+ return;
193
+ }
194
+
195
+ await interaction.reply({ content, flags: "Ephemeral" });
196
+ }
197
+ }
198
+ });
199
+ ```
200
+
201
+ Command-local hooks override `client.globals.hooks`, and `client.globals.hooks` override `defineGlobalCommandHooks()`.
202
+
203
+ ### BetterEmbed
204
+
205
+ ```ts
206
+ import { BetterEmbed, defineGlobalToolConfig } from "vimcord";
207
+
208
+ defineGlobalToolConfig({
209
+ embedColor: "#5865F2",
210
+ embedColorDev: "#FF9D00"
211
+ });
154
212
 
155
213
  const embed = new BetterEmbed({
156
- context: { interaction }, // Auto-context for tokens
157
- title: "Welcome, $USER!",
214
+ context: { interaction },
215
+ title: "Welcome, $USER_NAME",
158
216
  description: ["Your avatar: $USER_AVATAR", "Today is $MONTH/$DAY/$YEAR"],
159
- color: "#5865F2"
217
+ timestamp: true
160
218
  });
161
219
 
162
- // ACF (Auto Context Formatting) tokens available:
163
- // $USER, $USER_NAME, $DISPLAY_NAME, $USER_AVATAR
164
- // $BOT_AVATAR, $YEAR, $MONTH, $DAY, $INVIS
165
-
166
220
  await embed.send(interaction);
167
221
  ```
168
222
 
169
- ### The Paginator: Multi-Page Made Simple
223
+ ### BetterContainer And Components V2
224
+
225
+ ```ts
226
+ import { ButtonStyle } from "discord.js";
227
+ import { BetterContainer } from "vimcord";
228
+
229
+ const container = new BetterContainer({ color: "#5865F2" })
230
+ .addText("## Account linked")
231
+ .addText(["Your Discord account is connected.", "Use the dashboard button to manage settings."])
232
+ .addSection({
233
+ text: "Open your dashboard.",
234
+ button: {
235
+ customId: "dashboard:open",
236
+ label: "Dashboard",
237
+ style: ButtonStyle.Primary
238
+ }
239
+ });
240
+
241
+ await container.send(interaction);
242
+ ```
243
+
244
+ ### Paginator
170
245
 
171
246
  ```ts
172
- import { Paginator, PaginationType } from "vimcord";
247
+ import { BetterContainer, Paginator, PaginationTimeout, PaginationType } from "vimcord";
248
+
249
+ const intro = new BetterContainer().addText("## Help").addText("Choose a page below.");
250
+ const moderation = new BetterContainer().addText("## Moderation").addText("Ban, kick, and timeout commands.");
173
251
 
174
252
  const paginator = new Paginator({
175
- type: PaginationType.LongJump, // first | back | jump | next | last
176
- timeout: 60000
253
+ type: PaginationType.LongJump,
254
+ idle: 60_000,
255
+ onTimeout: PaginationTimeout.DisableComponents
177
256
  });
178
257
 
179
- // Add chapters (groups of pages)
180
258
  paginator
181
- .addChapter([helpPage1, helpPage2, helpPage3], { label: "General Help", emoji: "📖" })
182
- .addChapter([modPage1, modPage2], { label: "Moderation", emoji: "🛡️" });
259
+ .addChapter([intro, "Use `/help command` for command-specific help."], { label: "General", emoji: "📖" })
260
+ .addChapter([moderation], { label: "Moderation", emoji: "🛡️" });
183
261
 
184
- // Send it anywhere
185
- const message = await paginator.send(interaction);
186
-
187
- // Events for custom logic
188
- paginator.on("pageChange", (page, index) => {
189
- console.log(`User viewing page ${index.nested} of chapter ${index.chapter}`);
262
+ paginator.on("pageChange", (_page, index) => {
263
+ console.log(`Viewing chapter ${index.chapter}, page ${index.nested}`);
190
264
  });
265
+
266
+ await paginator.send(interaction);
191
267
  ```
192
268
 
193
- ### Prompt: Confirmation Dialogs
269
+ ### Prompt
194
270
 
195
271
  ```ts
196
- import { Prompt } from "vimcord";
272
+ import { BetterEmbed, promptMessage, ResolveAction } from "vimcord";
197
273
 
198
- const prompt = new Prompt({
274
+ const result = await promptMessage(interaction, {
199
275
  embed: new BetterEmbed({
276
+ context: { interaction },
200
277
  title: "Delete this message?",
201
- description: "This action cannot be undone.",
202
- context: { interaction }
278
+ description: "This action cannot be undone."
203
279
  }),
204
- timeout: 30000
280
+ participants: [interaction.user],
281
+ timeout: 30_000,
282
+ onResolve: [ResolveAction.DisableComponents],
283
+ highlightSelectedButton: true
205
284
  });
206
285
 
207
- await prompt.send(interaction);
208
- const result = await prompt.awaitResponse();
209
-
210
286
  if (result.confirmed) {
211
- await message.delete();
287
+ await targetMessage.delete();
212
288
  }
213
289
  ```
214
290
 
215
- ### BetterModal: V2 Components Done Right
291
+ ### BetterModal
216
292
 
217
293
  ```ts
294
+ import { TextInputStyle } from "discord.js";
218
295
  import { BetterModal } from "vimcord";
219
296
 
220
- const modal = new BetterModal({
221
- title: "Create Ticket",
222
- components: [
223
- {
224
- textInput: {
225
- label: "Subject",
226
- custom_id: "subject",
227
- style: TextInputStyle.Short,
228
- required: true
229
- }
230
- },
231
- {
232
- textInput: {
233
- label: "Description",
234
- custom_id: "description",
235
- style: TextInputStyle.Paragraph
236
- }
237
- }
238
- ]
239
- });
297
+ const modal = new BetterModal({ title: "Create Ticket" })
298
+ .addTextInput({
299
+ customId: "subject",
300
+ label: "Subject",
301
+ style: TextInputStyle.Short,
302
+ required: true
303
+ })
304
+ .addTextInput({
305
+ customId: "description",
306
+ label: "Description",
307
+ style: TextInputStyle.Paragraph,
308
+ required: false
309
+ });
240
310
 
241
- // Show and await in one call
242
- const result = await modal.showAndAwait(interaction);
311
+ const result = await modal.showAndAwait(interaction, { timeout: 60_000 });
312
+ if (!result) return;
243
313
 
244
- if (result) {
245
- const subject = result.getField("subject");
246
- await result.reply({
247
- content: `Ticket created: ${subject}`,
248
- flags: "Ephemeral"
249
- });
250
- }
314
+ const subject = result.getField<string>("subject", true);
315
+
316
+ await result.reply({
317
+ content: `Ticket created: ${subject}`,
318
+ flags: "Ephemeral"
319
+ });
251
320
  ```
252
321
 
253
- ### DynaSend: One Method, Every Context
322
+ ### DynaSend
254
323
 
255
324
  ```ts
256
- import { dynaSend } from "vimcord";
325
+ import { dynaSend, SendMethod } from "vimcord";
257
326
 
258
- // Works with: interactions, channels, messages, users
259
- // Automatically decides: reply? editReply? followUp? channel.send?
260
327
  await dynaSend(interaction, {
261
- content: "Hello!",
262
- embeds: [myEmbed],
263
- components: [actionRow]
328
+ sendMethod: interaction.deferred || interaction.replied ? SendMethod.EditReply : SendMethod.Reply,
329
+ content: "Hello from one send helper.",
330
+ embeds: [embed]
264
331
  });
265
332
  ```
266
333
 
267
- ### Database: MongoDB Without the Pain
334
+ ### MongoDB Plugin
268
335
 
269
336
  ```ts
270
- import { createMongoSchema, MongoDatabase } from "vimcord";
337
+ import { createMongoPlugin, createMongoSchema, MongoosePlugin } from "@vimcord/plugin-mongoose";
271
338
 
272
- // Define a schema with retry logic built-in
273
- const UserSchema = createMongoSchema("Users", {
339
+ client.use(
340
+ new MongoosePlugin({
341
+ connectionRefresh: {
342
+ interval: 60_000,
343
+ maxFailures: 2
344
+ }
345
+ })
346
+ );
347
+
348
+ const Users = createMongoSchema("Users", {
274
349
  userId: { type: String, required: true, unique: true },
275
- username: String,
276
350
  balance: { type: Number, default: 0 },
277
351
  createdAt: { type: Date, default: Date.now }
278
352
  });
279
353
 
280
- // CRUD operations with automatic retries
281
- const user = await UserSchema.fetch({ userId: "123" });
282
- await UserSchema.upsert({ userId: "123" }, { $inc: { balance: 100 } });
354
+ const user = await Users.fetch({ userId: "123" }, undefined, { required: true });
355
+ await Users.upsert({ userId: "123" }, { $inc: { balance: 100 } });
283
356
 
284
- // Create plugins for reusable logic
285
357
  const SoftDeletePlugin = createMongoPlugin(builder => {
286
358
  builder.schema.add({ deletedAt: { type: Date, default: null } });
287
359
  builder.extend({
@@ -291,73 +363,105 @@ const SoftDeletePlugin = createMongoPlugin(builder => {
291
363
  });
292
364
  });
293
365
 
294
- UserSchema.use(SoftDeletePlugin);
366
+ Users.use(SoftDeletePlugin);
295
367
  ```
296
368
 
297
- ### Logger: Terminal Output That Doesn't Suck
369
+ ### Client Logger
298
370
 
299
371
  ```ts
300
- import { Logger } from "vimcord";
301
-
302
- const logger = new Logger({
303
- prefix: "Economy",
304
- prefixEmoji: "💰",
305
- colors: { primary: "#57F287" }
306
- });
307
-
308
- logger.success("User purchased item");
309
- logger.table("Stats", { users: 150, revenue: "$420.69" });
372
+ client.logger.success("Startup complete");
310
373
 
311
- // Loader for async operations
312
- const stopLoader = logger.loader("Connecting to database...");
313
- await connectToDB();
314
- stopLoader("Connected!");
374
+ const stopLoader = client.logger.loader("Syncing commands...");
375
+ await client.modules.commands.push();
376
+ stopLoader("Commands synced");
315
377
  ```
316
378
 
317
379
  ---
318
380
 
319
381
  ## API Reference
320
382
 
321
- ### Client Creation
322
-
323
- | Export | Description |
324
- | --------------------------------------------- | ----------------------------- |
325
- | `createClient(options, features?, config?)` | Create a new Vimcord instance |
326
- | `Vimcord.create(options, features?, config?)` | Same as above |
327
- | `Vimcord.getInstance(id?)` | Get existing instance by ID |
328
- | `Vimcord.getReadyInstance(id?, timeout?)` | Wait for ready instance |
329
-
330
- ### Feature Flags
383
+ ### Core Exports
384
+
385
+ | Export | Description |
386
+ | --- | --- |
387
+ | `Vimcord` | Discord.js client subclass with modules, plugins, globals, status, and logging |
388
+ | `SlashCommandModule` | Slash command module with optional subcommand routes |
389
+ | `PrefixCommandModule` | Prefix command module with aliases and parsed message content |
390
+ | `MessageContextCommandModule` | Message context menu command module |
391
+ | `UserContextCommandModule` | User context menu command module |
392
+ | `EventModule` | Typed Discord event module |
393
+ | `BetterEmbed` | Embed helper with context formatting |
394
+ | `BetterContainer` | Components V2 container helper |
395
+ | `Paginator` | Component-based paginator |
396
+ | `BetterModal` | Modal helper with V2 component support |
397
+ | `promptMessage`, `promptModal` | Confirmation prompt helpers |
398
+ | `dynaSend` | Send helper for interactions, channels, messages, members, and users |
399
+ | `defineGlobalToolConfig` | Global UX defaults for embeds, collectors, paginator, and prompts |
400
+ | `defineGlobalCommandHooks` | Global command hooks used when modules do not define local hooks |
401
+
402
+ ### Client Options
331
403
 
332
404
  ```ts
333
- {
334
- useDefaultSlashCommandHandler: boolean; // Auto-handle slash commands
335
- useDefaultPrefixCommandHandler: boolean; // Auto-handle prefix commands
336
- useDefaultContextCommandHandler: boolean; // Auto-handle context commands
337
- useGlobalErrorHandlers: boolean; // Catch unhandled errors
338
- importModules: {
339
- events?: string | string[] | ModuleImportOptions;
340
- slashCommands?: string | string[] | ModuleImportOptions;
341
- prefixCommands?: string | string[] | ModuleImportOptions;
342
- contextCommands?: string | string[] | ModuleImportOptions;
343
- };
344
- maxLoginAttempts?: number; // Retry login on failure (default: 3)
345
- }
405
+ const client = new Vimcord({
406
+ customId: "main",
407
+ client: {
408
+ intents: [GatewayIntentBits.Guilds]
409
+ },
410
+ globals: {
411
+ app: {
412
+ name: "My Bot",
413
+ devMode: false,
414
+ verbose: false,
415
+ disableBanner: false
416
+ },
417
+ staff: {
418
+ ownerId: "123456789012345678",
419
+ superUsers: [],
420
+ superUserRoles: []
421
+ }
422
+ },
423
+ connectionRefresh: {
424
+ interval: 60_000,
425
+ requestTimeout: 10_000,
426
+ maxFailures: 2,
427
+ maxRefreshAttempts: 3
428
+ },
429
+ logLevel: "debug",
430
+ verbose: false
431
+ });
346
432
  ```
347
433
 
348
- ### Configuration
434
+ ### Module Loading
349
435
 
350
436
  ```ts
351
- client.configure("app", {
352
- name: "MyBot", // Display name
353
- version: "1.0.0", // Version string
354
- devMode: false, // Use TOKEN_DEV env var
355
- verbose: false // Extra logging
437
+ await client.modules.load({
438
+ slashCommands: { dir: "./src/commands/slash", suffix: ".slash" },
439
+ prefixCommands: { dir: "./src/commands/prefix", suffix: ".prefix" },
440
+ messageContextCommands: { dir: "./src/commands/message-context", suffix: ".mctx" },
441
+ userContextCommands: { dir: "./src/commands/user-context", suffix: ".uctx" },
442
+ events: { dir: "./src/events", suffix: ".event" }
356
443
  });
444
+ ```
445
+
446
+ ### Runtime Configuration
357
447
 
358
- client.configure("staff", {
359
- ownerId: "123456", // Bot owner
360
- staffRoleIds: ["..."] // Staff roles
448
+ ```ts
449
+ client.configure({
450
+ app: {
451
+ name: "My Bot",
452
+ devMode: true
453
+ },
454
+ staff: {
455
+ ownerId: "123456789012345678",
456
+ superUserRoles: ["987654321098765432"]
457
+ },
458
+ hooks: {
459
+ prefix: {
460
+ async onError({ message, error }) {
461
+ await message.reply(`Command failed: ${error?.message ?? "Unknown error"}`);
462
+ }
463
+ }
464
+ }
361
465
  });
362
466
  ```
363
467
 
@@ -367,45 +471,62 @@ client.configure("staff", {
367
471
 
368
472
  ### Status Rotation
369
473
 
474
+ Status config is user-owned. Vimcord does not provide a default status rotation.
475
+
370
476
  ```ts
371
- client.status.setRotation(
372
- [
373
- { name: "with commands", type: ActivityType.Playing },
374
- { name: "over {guilds} servers", type: ActivityType.Watching }
375
- ],
376
- { interval: 30000 }
377
- ); // Rotate every 30s
477
+ import { ActivityType } from "discord.js";
478
+
479
+ await client.status.set({
480
+ production: {
481
+ interval: 30_000,
482
+ randomize: true,
483
+ activity: [
484
+ { name: "$GUILD_COUNT servers", type: ActivityType.Watching, status: "online" },
485
+ { name: "Need help? Use /help", type: ActivityType.Custom, status: "online" }
486
+ ]
487
+ },
488
+ development: {
489
+ activity: { name: "In development", type: ActivityType.Custom, status: "dnd" }
490
+ }
491
+ });
378
492
  ```
379
493
 
380
- ### Event Handler
494
+ ### Command Dispatch Events
495
+
496
+ ```ts
497
+ import { Events } from "discord.js";
498
+ import { EventModule } from "vimcord";
499
+
500
+ export default new EventModule({
501
+ name: "dispatchInteractions",
502
+ event: Events.InteractionCreate,
503
+ async execute({ client, args: [interaction] }) {
504
+ await client.modules.commands.dispatchInteraction(interaction);
505
+ }
506
+ });
507
+ ```
381
508
 
382
509
  ```ts
383
- import { EventBuilder } from "vimcord";
510
+ import { Events } from "discord.js";
511
+ import { EventModule } from "vimcord";
384
512
 
385
- export default new EventBuilder({
513
+ export default new EventModule({
514
+ name: "dispatchPrefixCommands",
386
515
  event: Events.MessageCreate,
387
- execute: async (client, message) => {
516
+ async execute({ client, args: [message] }) {
388
517
  if (message.author.bot) return;
389
- // Handle message
518
+ await client.modules.commands.dispatchMessage(message, ["!", "?"]);
390
519
  }
391
520
  });
392
521
  ```
393
522
 
394
- ### Error Handling
523
+ ### Bot Staff Checks
395
524
 
396
525
  ```ts
397
- // Vimcord automatically catches command errors and sends user-friendly messages
398
- // Configure what happens on error:
399
-
400
- client.configure("slashCommands", {
401
- errorMessage: "❌ Something went wrong! Our team has been notified.",
402
- logErrors: true
403
- });
404
-
405
- // Or handle specific errors:
406
- process.on("unhandledRejection", error => {
407
- client.logger.error("Unhandled rejection", error as Error);
408
- });
526
+ const isStaff = await client.isBotStaff(interaction.user.id);
527
+ if (!isStaff) {
528
+ await interaction.reply({ content: "This is staff-only.", flags: "Ephemeral" });
529
+ }
409
530
  ```
410
531
 
411
532
  ---
@@ -413,12 +534,11 @@ process.on("unhandledRejection", error => {
413
534
  ## Environment Variables
414
535
 
415
536
  ```bash
416
- # Required
417
537
  TOKEN=your_production_bot_token
418
- TOKEN_DEV=your_development_bot_token # Used when devMode: true
538
+ TOKEN_DEV=your_development_bot_token
419
539
 
420
- # MongoDB (optional)
421
540
  MONGO_URI=mongodb://localhost:27017/discord-bot
541
+ MONGO_URI_DEV=mongodb://localhost:27017/discord-bot-dev
422
542
  ```
423
543
 
424
544
  ---
@@ -435,7 +555,7 @@ Built on top of [qznt](https://github.com/xsqu1znt/qznt) for that extra bit of u
435
555
 
436
556
  <div align="center">
437
557
 
438
- Built with 💜 for the Discord.js community
558
+ Built with love for the Discord.js community
439
559
 
440
560
  Found a bug? [Open an issue](https://github.com/xsqu1znt/vimcord/issues)
441
561