{"_rev":"11-e767a724310377908d06084541b589b1","time":{"created":"2026-06-30T08:33:15.260Z","modified":"2026-06-30T08:33:15.818Z","1.0.0":"2025-04-05T16:52:50.260Z","1.0.1":"2025-04-05T17:04:50.567Z","1.0.2":"2025-04-05T17:09:27.878Z","1.0.3":"2025-04-05T17:16:51.753Z","1.0.4":"2025-04-05T17:18:47.566Z","1.0.5":"2025-04-05T17:21:33.220Z","1.0.6":"2025-04-05T17:27:30.208Z","1.0.7":"2025-04-06T10:06:00.443Z","3.0.0":"2026-06-30T08:33:15.563Z"},"_id":"@dookdiks/discord-bot-builder","name":"@dookdiks/discord-bot-builder","dist-tags":{"latest":"3.0.0"},"versions":{"3.0.0":{"name":"@dookdiks/discord-bot-builder","publishConfig":{"access":"public"},"version":"3.0.0","description":"Builder-first TypeScript framework for Discord bots — fluent APIs for commands, components, config, database, middleware, and more","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.examples.json","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run build && npm run test"},"keywords":["discord","bot","builder","discord.js","typescript","oop"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/DookDiks/discord-bot-builder.git"},"bugs":{"url":"https://github.com/DookDiks/discord-bot-builder/issues"},"homepage":"https://github.com/DookDiks/discord-bot-builder#readme","peerDependencies":{"discord.js":"^14.0.0"},"dependencies":{"zod":"^4.4.3"},"devDependencies":{"discord.js":"^14.26.4","tsup":"^8.5.0","typescript":"^5.8.3","vitest":"^3.2.4"},"engines":{"node":">=18"},"gitHead":"c781f4fffcac7ce2f4ae03688be54c6f779f9430","_id":"@dookdiks/discord-bot-builder@3.0.0","_nodeVersion":"26.3.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-umMbybO7PGAMUDCBVvwvqSlj8Ud09gH2bkP/ULfDh7VVdGJ4HmYYco7H++SfgsuC/31h0EQWwQsUPqbVysN71Q==","shasum":"d0c52776703071c7fc6b8ea4040ec3a3f40f70c1","tarball":"https://registry.npmjs.org/@dookdiks/discord-bot-builder/-/discord-bot-builder-3.0.0.tgz","fileCount":8,"unpackedSize":613500,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIF31EZ5DSdlUQJzwao0RYTTPhVJLwFYQouTCPM6Z2vGoAiBMEAlJFeno6ts9pY7Mm8LH49EwiM3kXn+Mzt8s8lWRZA=="}]},"_npmUser":{"name":"svacmai","email":"svac.mai@gmail.com"},"directories":{},"maintainers":[{"name":"svacmai","email":"svac.mai@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/discord-bot-builder_3.0.0_1782808395425_0.9716968597004403"},"_hasShrinkwrap":false}},"maintainers":[{"name":"svacmai","email":"svac.mai@gmail.com"}],"description":"Builder-first TypeScript framework for Discord bots — fluent APIs for commands, components, config, database, middleware, and more","homepage":"https://github.com/DookDiks/discord-bot-builder#readme","keywords":["discord","bot","builder","discord.js","typescript","oop"],"repository":{"type":"git","url":"git+https://github.com/DookDiks/discord-bot-builder.git"},"bugs":{"url":"https://github.com/DookDiks/discord-bot-builder/issues"},"license":"MIT","readme":"# discord-bot-builder\n\nProduction-ready, type-safe **builder-first** framework for Discord bots on Node.js and discord.js v14. Every part of the stack — config, logging, database, commands, components, middleware, and plugins — has a fluent builder API designed to be simpler than wiring discord.js directly.\n\n**Repository:** [github.com/DookDiks/discord-bot-builder](https://github.com/DookDiks/discord-bot-builder)\n\n## Installation\n\n```bash\nnpm install @dookdiks/discord-bot-builder discord.js\n```\n\nRequires Node.js 18+.\n\n## Quick start\n\n```ts\nimport {\n  BotBuilder,\n  CommandBuilder,\n  ConfigBuilder,\n  DatabaseBuilder,\n  LoggerBuilder,\n  MiddlewareBuilder,\n} from \"@dookdiks/discord-bot-builder\";\n\nconst ping = new CommandBuilder(\"ping\", \"Health check\")\n  .execute(async (ctx) => {\n    await ctx.reply({ content: \"Pong!\", ephemeral: true });\n  });\n\nconst bot = await BotBuilder.create()\n  .configure(ConfigBuilder.fromEnv())\n  .withLogger(LoggerBuilder.production(\"my-bot\"))\n  .database(DatabaseBuilder.memory())\n  .middleware(MiddlewareBuilder.defaults())\n  .command(ping)\n  .onReady((ctx) => ctx.services.logger.info(`Ready: ${ctx.client.user?.tag}`))\n  .build();\n\nawait bot.start();\n```\n\n## Builder catalog\n\nEvery major feature exposes a `.create()` / fluent builder. Pass any builder to `BotBuilder` — it resolves via `.build()` automatically.\n\n| Builder | Purpose |\n|---------|---------|\n| `BotBuilder` | Main entry — wires everything and produces `BuiltBot` |\n| `ConfigBuilder` | Token, client ID, prefix, owners, deploy flags |\n| `LoggerBuilder` | Log level, prefix, timestamps |\n| `DatabaseBuilder` | Memory, file, or custom adapter (Prisma, Drizzle, …) |\n| `MiddlewareBuilder` | Global middleware chain (`.defaults()` = log + errors) |\n| `PluginBuilder` | Plugin lifecycle (`setup`, `onReady`, `onStart`, `onStop`) |\n| `CommandBuilder` | Slash commands with options, cooldowns, permissions |\n| `CommandGroupBuilder` | Subcommands and nested subcommand groups |\n| `SubcommandBuilder` | Individual subcommand inside a group |\n| `MessageCommandBuilder` | Prefix commands with aliases |\n| `UserContextMenuBuilder` | Right-click user commands |\n| `MessageContextMenuBuilder` | Right-click message commands |\n| `EventBuilder` | Discord client events |\n| `ButtonHandlerBuilder` | Button interaction handlers |\n| `SelectMenuHandlerBuilder` | Select menu handlers |\n| `ModalHandlerBuilder` | Modal submit handlers |\n| `ButtonRowBuilder` | Discord button UI rows |\n| `SelectMenuRowBuilder` | String select menu UI rows |\n| `ModalFormBuilder` | Modal form UI |\n| `EmbedBuilder` | Rich embed messages |\n| `IntentBuilder` | Gateway intents |\n| `PermissionBuilder` | Role, owner, member, and bot permissions |\n| `CooldownBuilder` | Per-user / guild / channel cooldowns |\n| `PaginatorBuilder` | Paginated messages with prev/next/stop |\n\n### Supporting utilities\n\n| Export | Purpose |\n|--------|---------|\n| `CommandRegistry` | Register and deploy slash + context menu commands |\n| `ComponentRegistry` | Register button, select, and modal handlers |\n| `EventRegistry` | Register client events |\n| `loadConfigFromEnv` / `validateConfig` | Env-based or object config validation (Zod) |\n| `createAdapter` | Wrap Prisma, Drizzle, or any client as `DatabaseAdapter` |\n| `checkPermissions` | Permission check helper used by the router |\n| `sendPaginator` | Low-level paginator (prefer `PaginatorBuilder`) |\n| `resolveBuildable` | Resolve `T \\| { build(): T }` in custom code |\n\n## Full bot example\n\n```ts\nimport {\n  BotBuilder,\n  CommandBuilder,\n  CommandGroupBuilder,\n  ButtonHandlerBuilder,\n  ButtonRowBuilder,\n  EmbedBuilder,\n  ConfigBuilder,\n  DatabaseBuilder,\n  LoggerBuilder,\n  MiddlewareBuilder,\n  IntentBuilder,\n  PermissionBuilder,\n  CooldownBuilder,\n} from \"@dookdiks/discord-bot-builder\";\n\nconst mod = new CommandGroupBuilder(\"mod\", \"Moderation\")\n  .guildOnly()\n  .subcommand(\"ban\", \"Ban a user\", (b) =>\n    b.addUserOption(\"user\", \"Target\", { required: true })\n      .permissions(PermissionBuilder.role(\"mod\").member(\"BanMembers\"))\n      .cooldownConfig(CooldownBuilder.ofSeconds(5).perGuild())\n      .execute(async (ctx) => {\n        await ctx.reply({ content: `Banned <@${(ctx.options.user as { id: string }).id}>` });\n      }),\n  );\n\nconst panel = new CommandBuilder(\"panel\", \"Open settings panel\")\n  .execute(async (ctx) => {\n    const row = new ButtonRowBuilder()\n      .danger(\"confirm:delete\", \"Delete\")\n      .secondary(\"confirm:cancel\", \"Cancel\")\n      .build();\n    await ctx.reply({ content: \"Choose:\", components: [row] });\n  });\n\nconst confirm = ButtonHandlerBuilder.create(\"confirm\")\n  .prefix()\n  .execute(async (ctx) => {\n    const action = ctx.customId.split(\":\")[1];\n    await ctx.update({\n      content: action === \"delete\" ? \"Deleted!\" : \"Cancelled.\",\n      components: [],\n    });\n  });\n\nconst bot = await BotBuilder.create()\n  .configure(ConfigBuilder.fromEnv())\n  .withLogger(LoggerBuilder.production(\"my-bot\"))\n  .database(DatabaseBuilder.create().file(\"./data/store.json\"))\n  .intents(IntentBuilder.default().members().moderation())\n  .middleware(MiddlewareBuilder.defaults())\n  .commands([mod, panel])\n  .button(confirm)\n  .onReady((ctx) => {\n    ctx.services.logger.info(`Logged in as ${ctx.client.user?.tag}`);\n  })\n  .build();\n\nawait bot.start();\n```\n\n## Commands\n\n### Slash commands\n\n```ts\nnew CommandBuilder(\"greet\", \"Greet someone\")\n  .addUserOption(\"user\", \"Who to greet\")\n  .addStringOption(\"message\", \"Custom text\", { autocomplete: true })\n  .cooldown(3)\n  .guildOnly()\n  .defer(true)\n  .ephemeral()\n  .permissions(PermissionBuilder.role(\"member\"))\n  .autocomplete(async (ctx) => {\n    await ctx.respond([{ name: \"Hello\", value: \"hello\" }]);\n  })\n  .execute(async (ctx) => { ... });\n```\n\nSupported options: `string`, `integer`, `number`, `boolean`, `user`, `channel`, `role`, `mentionable`, `attachment`.\n\n### Subcommands\n\n```ts\nnew CommandGroupBuilder(\"economy\", \"Economy\")\n  .group(\"wallet\", \"Wallet\", (g) =>\n    g.subcommand(\"balance\", \"Check balance\", (b) => b.execute(async () => {}))\n      .subcommand(\"deposit\", \"Deposit coins\", (b) => b.execute(async () => {})),\n  );\n```\n\n### Prefix commands\n\n```ts\nnew MessageCommandBuilder(\"help\", \"Show help\")\n  .aliases(\"h\", \"?\")\n  .cooldown(5, \"channel\")\n  .guildOnly()\n  .permissions(PermissionBuilder.owner(\"YOUR_USER_ID\"))\n  .execute(async (ctx) => { ... });\n```\n\n### Context menus\n\n```ts\nnew UserContextMenuBuilder(\"View Profile\").execute(async (ctx) => { ... });\nnew MessageContextMenuBuilder(\"Quote Message\").execute(async (ctx) => { ... });\n```\n\n## Components\n\n**UI builders** create discord.js components to send in replies:\n\n```ts\nnew ButtonRowBuilder().primary(\"ok\", \"OK\").danger(\"no\", \"Cancel\").build();\nnew SelectMenuRowBuilder(\"pick\").addOption(\"A\", \"a\").addOption(\"B\", \"b\").build();\nnew ModalFormBuilder(\"form\", \"Title\").textInput(\"name\", \"Name\").build();\n```\n\n**Handler builders** register interaction callbacks on the bot:\n\n```ts\nButtonHandlerBuilder.create(\"page\").prefix().execute(async (ctx) => { ... });\nSelectMenuHandlerBuilder.create(\"role\").execute(async (ctx) => { ... });\nModalHandlerBuilder.create(\"signup\").execute(async (ctx) => { ... });\n```\n\nPrefix matching (`\"page\"` matches `\"page:2\"`) is enabled with `.prefix()`.\n\n## Database\n\n```ts\n// In-memory (dev / tests)\nDatabaseBuilder.memory()\n\n// JSON file persistence\nDatabaseBuilder.create().file(\"./data/bot.json\").build()\n\n// Prisma / Drizzle / custom\nDatabaseBuilder.create().custom(prisma, {\n  connect: () => prisma.$connect(),\n  disconnect: () => prisma.$disconnect(),\n}).build()\n```\n\nExtend `MemoryAdapter` or `FileAdapter` for typed helpers (see `examples/database-bot/`).\n\n## Config\n\n```ts\n// From environment (validates with Zod)\nConfigBuilder.fromEnv().build()\n\n// Explicit values\nConfigBuilder.create()\n  .token(\"...\")\n  .clientId(\"...\")\n  .guildId(\"...\")           // optional — fast dev deploy\n  .prefix(\"!\")\n  .ownerIds(\"123\", \"456\")\n  .registerCommandsGlobally(false)\n  .deployCommandsOnStart(true)\n  .logLevel(\"info\")\n  .build()\n```\n\n### Environment variables\n\n| Variable | Required | Default |\n|----------|----------|---------|\n| `DISCORD_TOKEN` or `BOT_TOKEN` | Yes | — |\n| `CLIENT_ID` or `DISCORD_CLIENT_ID` | Yes | — |\n| `GUILD_ID` | No | — |\n| `BOT_PREFIX` | No | `!` |\n| `REGISTER_COMMANDS_GLOBALLY` | No | `false` |\n| `DEPLOY_COMMANDS_ON_START` | No | `true` |\n| `LOG_LEVEL` | No | `info` |\n| `GRACEFUL_SHUTDOWN` | No | `true` |\n| `BOT_OWNER_IDS` | No | — |\n\n## Middleware\n\n```ts\nMiddlewareBuilder.create()\n  .log()              // log each command\n  .handleErrors()     // catch and reply on failure\n  .owners(\"123\")     // restrict following middleware / halt\n  .defer(true)        // auto-defer replies\n  .use(myCustomMw)    // custom middleware\n  .build()\n\n// Shorthand\nMiddlewareBuilder.defaults()  // .log().handleErrors()\n```\n\nPer-command middleware: `.use(mw)` on `CommandBuilder` or `CommandGroupBuilder`.\n\n## Plugins\n\n```ts\nPluginBuilder.create(\"analytics\")\n  .setup((builder) => builder.command(statsCmd))\n  .onReady(async (services) => { ... })\n  .onStart(async (services) => { ... })\n  .onStop(async (services) => { ... })\n  .build()\n```\n\n## Embeds & pagination\n\n```ts\nEmbedBuilder.success(\"Done\", \"All good\").build()\nEmbedBuilder.error(\"Failed\").field(\"Reason\", \"timeout\").build()\n\nawait PaginatorBuilder.create()\n  .page(\"Page 1\")\n  .page(\"Page 2\")\n  .timeout(60_000)\n  .forUser(userId)\n  .send(message);\n```\n\n## Production lifecycle\n\n```ts\nconst bot = await BotBuilder.create()...build();\n\nawait bot.start();          // connect DB, deploy commands, login\nawait bot.deployCommands(); // deploy without starting (CI/CD)\nconst health = bot.getHealth();  // uptime, guilds, status, …\nawait bot.stop();           // graceful shutdown (SIGINT/SIGTERM handled by default)\n```\n\n## Examples\n\n| Example | Description |\n|---------|-------------|\n| [basic-bot](./examples/basic-bot/) | Ping, greet, builder defaults |\n| [database-bot](./examples/database-bot/) | Custom typed `MemoryAdapter` store |\n| [production-bot](./examples/production-bot/) | Full builder showcase |\n\n## Scripts\n\n```bash\nnpm run build       # Compile ESM + CJS + types\nnpm run typecheck   # Typecheck src + examples\nnpm test            # Run 152 unit tests\nnpm run dev         # Watch mode\n```\n\n## Production checklist\n\n- [ ] Use `ConfigBuilder.fromEnv()` — never hardcode tokens\n- [ ] Use `DatabaseBuilder.create().file()` or `.custom(prisma)` — not memory in prod\n- [ ] Add `MiddlewareBuilder.defaults()` globally\n- [ ] Set `.ownerIds()` for admin commands\n- [ ] Use `guildId` in dev, `registerCommandsGlobally(true)` in production\n- [ ] Run `bot.deployCommands()` in CI before deploy\n- [ ] Enable `gracefulShutdown(true)` (default)\n- [ ] Monitor with `bot.getHealth()`\n\n## License\n\nMIT\n","readmeFilename":"README.md"}