A Discord bot secretary for a small private server with friends — the n-th circle of hell. Built with TypeScript, Discord.js v14, and MongoDB, with a Next.js web dashboard.
- Slash commands
- Birthday tracking and reminders
- D-Day countdowns
- Quote collection from messages
- Nickname detection and automatic mentioning
- Scheduled reminders with timezone support
- Message translation (reply with
translate/tr/tl) - Web dashboard for analytics and data management
- Per-guild persistent storage
- pnpm monorepo: the bot, the web dashboard, and a shared types package
- Strict TypeScript with ESM
- Clear separation between Discord I/O and business logic
- Services hold the business logic; only the scheduler talks to Discord directly
- MongoDB schema design favors per-guild isolation
- The dashboard talks to a Hono API (REST + SSE) that runs as its own process, separate from the bot
- Writes from either process are recorded in a capped MongoDB collection (
changefeed), which the API follows to push live updates to the dashboard
- Node.js v24.14+ (required for
#/subpath imports; pnpm refuses to run on older versions) - pnpm
- MongoDB
- A Discord application with a bot token
-
Clone the repository
git clone https://github.com/MysticalWizard/electricalwizard.git cd electricalwizard -
Install dependencies
pnpm install
-
Configure environment variables
cp .env.example .env
Edit
.envwith your values:BOT_TOKEN=YOUR_BOT_TOKEN CLIENT_ID=YOUR_APPLICATION_ID GUILD_ID=YOUR_SERVER_ID OWNER_ID=YOUR_USER_ID MONGODB_HOST=localhost:27017 MONGODB_DB=electricalwizard # MONGODB_USER= # MONGODB_PASSWORD= # Translation service TRANSLATION_API_KEY=YOUR_TRANSLATION_API_KEY # TRANSLATION_API_URL=https://lang.mystwiz.net # Web dashboard DISCORD_CLIENT_SECRET=YOUR_DISCORD_CLIENT_SECRET WEB_PORT=7611 # At least 32 random characters, e.g. from `openssl rand -base64 32` WEB_SESSION_SECRET= WEB_BASE_URL=http://localhost:8450This project assumes a locally running MongoDB instance by default. Authentication is optional and can be enabled via
MONGODB_USER/MONGODB_PASSWORD.The translation feature requires
TRANSLATION_API_KEY. The dashboard's Discord OAuth login requiresDISCORD_CLIENT_SECRETand a session secret.WEB_BASE_URLis the address you open the dashboard at: add<WEB_BASE_URL>/auth/callbackas an OAuth2 redirect in the Discord developer portal, and the API only accepts changes from that origin. -
Deploy slash commands
pnpm build && pnpm deploy # production (runs the compiled dist/deploy.js) pnpm deploy:dev # development (runs src/ directly, no build needed)
This registers all slash commands with Discord. Commands marked as global are available everywhere; guild commands are registered to
GUILD_ID. -
Start the bot
# Development (hot reload) pnpm dev # bot only pnpm dev:web # dashboard API only pnpm dev:dashboard # dashboard only pnpm dev:all # bot + API + dashboard together # Production pnpm build:all # build bot, API and dashboard (or `pnpm build` for bot + API) pnpm start # start the bot pnpm start:web # start the dashboard API
The dashboard API is its own process (
src/web/index.ts, built todist/web/index.js) listening onWEB_PORT; PM2 runs it alongside the bot and dashboard (see below). The Next.js dashboard runs on port 8450 and proxies/apiand/authtohttp://127.0.0.1:7611, so keepWEB_PORT=7611unless you also changedashboard/next.config.ts.
The bot includes PM2 scripts for process management. ecosystem.config.cjs defines three processes: the bot, the dashboard API, and the dashboard.
pnpm pm2:start # Start all three processes
pnpm pm2:stop # Stop them
pnpm pm2:restart # Restart them
pnpm pm2:logs # View logs
pnpm pm2:delete # Remove them from PM2These scripts are intended for simple single-instance deployments.
The API and, under PM2, the dashboard listen on 127.0.0.1 only. Serve the
dashboard through a reverse proxy on the same machine (nginx, Caddy,
cloudflared...) pointed at 127.0.0.1:8450, which also terminates HTTPS.
Make sure to run pnpm build:all before using PM2 commands.
src/
commands/ # Slash command definitions
events/ # Discord event handlers
models/ # Mongoose schemas (User, Guild, Quote...)
services/ # Business logic (database, nickname, scheduler, translation...)
utils/ # Shared utilities (autocomplete, embeds, loaders...)
web/ # Hono dashboard API, run as its own process (routes, auth/session, SSE)
config.ts # Environment configuration
deploy.ts # Command registration script
main.ts # Bot entry point
types.ts # TypeScript interfaces
enums.ts # Enum definitions
dashboard/ # Next.js web dashboard (App Router)
packages/
shared/ # Types shared between the bot and the dashboard
pnpm lint # Run ESLint (bot + API)
pnpm lint:fix # Fix lint issues
pnpm lint:all # Lint bot + dashboard
pnpm format # Format with Prettier
pnpm format:check # Check formattingA pre-commit hook (via Husky and lint-staged) automatically lints and formats staged files.
Internal imports use Node subpath imports (#/services/...), defined in package.json. They resolve to src/ under the development condition and to dist/ otherwise. The pnpm dev scripts pass --conditions=development; add it yourself if you run tsx directly, or imports will load the compiled code in dist/.
When inviting the bot to a server, it needs the following gateway intents:
- Guilds - Track guild membership
- Guild Members - Access member lists and user data
- Guild Messages - Read messages for quote capture and nickname detection
- Message Content - Read message content (privileged intent)
- Runtime: Node.js with ESM modules
- Language: TypeScript 7 (strict mode)
- Discord Library: discord.js v14
- Database: MongoDB via Mongoose
- Dashboard API: Hono
- Date Handling: Day.js
- Dashboard: Next.js 16 + React 19, TanStack Query/Table, Recharts, Radix UI
- Monorepo: pnpm workspaces
- Tooling: ESLint, Prettier, Husky, lint-staged, tsx
This project is licensed under the MIT License - see the LICENSE file for details.