Modern, modular, and fast. Batteries included: commands, events, components, jobs, modules, cooldowns, structured logs, optional metrics, i18n, and pluggable persistence (memory/SQLite/Redis). Works unsharded or sharded.
- Features
- Architecture
- Requirements
- Quick Start
- Configuration
- Bot Setup (Portal, Scopes, Permissions, Intents)
- Scripts
- Scaffolding (Create Features Fast)
- Logging
- Metrics
- Persistence
- i18n
- Sharding
- CI
- Troubleshooting
- Contributing
- License
- Modular loaders for commands, events, components, jobs, modules
- Slash + Context Menu commands with cooldowns and per-command guards
- Component router for buttons, selects, modals with safe fallbacks
- Structured logging via pino with shard labels and pretty dev output
- Optional Prometheus metrics at
/metrics - Pluggable store: memory, SQLite, or Redis
- Basic i18n with per-guild locale and a minimal
t()helper - Sharding-ready with unified dev/prod entry
- DX niceties: ESM, fast builds with tsup,
.editorconfig,.nvmrc, and copy-ready templates
src/
commands/ slash + context menu commands
components/ button/select/modal handlers (idPrefix-based routing)
core/ logger, env, loader, metrics, store
deploy/ command registration script
events/ discord.js events
i18n/ translation bundles and helper
jobs/ long-running tasks or schedulers
modules/ feature initializers
utils/ helpers (shard label, respond)
index.ts app entry (single process)
shard.ts sharded entry
The loader scans folders and auto-registers. Guards check owner/user/bot perms and cooldowns before execution.
- Node 20.x
- Discord application with a Bot Token
Windows (PowerShell):
winget install Schniz.fnm
fnm env --use-on-cd | Out-String | Invoke-Expression
"fnm env --use-on-cd | Out-String | Invoke-Expression" | Add-Content $PROFILE
fnm install 20
fnm use 20
node -vmacOS/Linux:
curl -fsSL https://fnm.vercel.app/install | bash
fnm install 20
fnm use 20
node -vgit clone https://github.com/WannaBeIan/DiscordBotTemplate
cd DiscordBotTemplate
npm i
cp .env.example .envSet these in .env:
DISCORD_TOKEN=
DISCORD_CLIENT_ID=
DEFAULT_GUILD_ID=
Deploy and run:
npm run deploy:guild
npm run devBuild and run:
npm run build
npm startSharded (optional):
npm run dev:sharded
npm run start:shardedCopy .env.example → .env and fill:
DISCORD_TOKEN=
DISCORD_CLIENT_ID=
DEFAULT_GUILD_ID=
NODE_ENV=development
OWNER_IDS=
STORE_BACKEND=memory
SQLITE_PATH=./data/bot.db
REDIS_URL=
LOCALE_DEFAULT=en-US
METRICS_PORT=0
STORE_BACKEND: memory | sqlite | redis
METRICS_PORT: 0 disables; set a port to expose /metrics
OWNER_IDS: comma-separated IDs for owner-only commands
Developer Portal
- Installation → Guild Install enabled
- Authorization Method: None
- Scopes:
botandapplications.commands
Permissions integer
- Minimal:
2147568640 -
- Attach Files:
2147601408
- Attach Files:
-
- Reactions:
2147601472
- Reactions:
Gateway Intents
- Required: Guilds
- Common: Guild Members
- Optional: Message Content, Presence
Invite URL template:
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&scope=bot%20applications.commands&permissions=2147568640
{
"build": "tsup",
"dev": "tsx watch src/index.ts",
"dev:sharded": "tsx src/shard.ts",
"start": "node -e \"process.env.RUNTIME_DIR='dist'; import('./dist/index.js')\"",
"start:sharded": "node -e \"process.env.RUNTIME_DIR='dist'; import('./dist/shard.js')\"",
"deploy:global": "tsx src/deploy/deploy-commands.ts --global",
"deploy:guild": "tsx src/deploy/deploy-commands.ts --guild"
}Duplicate templates and rename:
src/commands/templateCommand.tssrc/components/templateComponent.ts(kind: button | select | modal;idPrefixbefore the:in customId)src/events/templateEvent.tssrc/jobs/sampleJob.tssrc/modules/sampleModule.ts
After adding/removing commands:
npm run deploy:guildDev: pretty, one-line, colored output with shard label [S0].
Prod: JSON structured logs.
Prometheus metrics at /metrics when METRICS_PORT > 0.
Counters and gauges:
bot_commands_total{name}bot_components_total{kind}bot_errors_total{type}bot_ws_ping_msbot_guildsbot_users_cache
Sharded behavior:
- Default: serve metrics only on shard 0
- Optional: per-shard mode with
basePort + shardId
Run:
METRICS_PORT=3000 npm start
curl http://localhost:3000/metricsChoose backend:
memoryfor devsqlitefor a single-file DB atSQLITE_PATHredisfor shared state across shards (REDIS_URL)
Guild locale is stored at guild:<id>:locale. Extend using the KeyValueStore interface.
- Bundles in
src/i18n/i18n.ts t(key, locale, vars?)resolves strings with{var}substitution/localegets/sets per-guild locale
Add languages by extending the bundles object.
src/shard.ts spawns workers. Logs include shard labels from the first line.
GitHub Actions included:
ci.yml— build on push/PRdeploy-commands.yml— manual command deploy with repo secrets
Set repo secrets:
DISCORD_TOKENDISCORD_CLIENT_IDDEFAULT_GUILD_ID(for guild deploy)
TokenInvalid Use the Bot Token, not Client Secret or Public Key.
Integration requires code grant Disable “Require OAuth2 Code Grant” and ensure Installation settings match.
This interaction failed Component not acked in time or unknown customId. The router safely handles unknown buttons/selects; rebuild if you changed routes.
50001 Missing Access vs 50013 Missing Permissions 50001: bot cannot view the channel. 50013: bot can view but lacks a specific permission. Use channel sends to test real perms; interaction replies may bypass some checks.
ERR_MODULE_NOT_FOUND in prod
Ensure code imports from dist in prod. tsup must include all entries.
TS: Property 'commands' does not exist on Client
Cast to ExtendedClient when accessing internal registries.
TS: editReply type mismatch
Strip ephemeral/flags when editing, or use the safeReply helper.
- Fork and create a feature branch
- Keep code minimal and optimized
- Run:
npm ci
npm run build
npm run deploy:guild
npm run dev- Update README and
.env.examplefor behavioral or env changes - Open a PR with clear testing steps
MIT — see LICENSE.




