Hans is built with a modular architecture that makes it easy to add and remove functionality on the fly, empowering you to create a bot tailored to your community's needs.
Built with Discord.JS, TypeScript, Postgres 17, Drizzle ORM, and lots of ❤️
Bring Hans to your Discord server and start using his available features immediately here 🔗. It uses the latest hans:nightly image with the latest features.
The list of commands & plugins can be found here 🔗.
🪬 NOTE: Please consider opening an issue and PR for bugs, suggestions, or new features.
yarn install
cp .env.template .env
# fill in DISCORD_TOKEN, DISCORD_CLIENT_ID, CRYPTO_KEY, CRYPTO_IV, BOT_GUILD_IDTo create your Discord application, visit the Developer Portal.
Docker is required for local development — the dev script spins up a local Postgres 17 container automatically.
Hans uses Postgres 17 with Drizzle ORM.
Schema is defined in TypeScript at src/db/schema.ts
and migrations are auto-generated into drizzle/.
yarn db:up # start the dev Postgres + apply migrations
yarn db:psql # psql into the dev DB
yarn db:studio # open Drizzle Studio at https://local.drizzle.studio
yarn db:down # stop the dev DB (data preserved)
yarn db:reset # wipe and recreate from scratchWhen you change the schema:
# 1. Edit src/db/schema.ts
# 2. Generate a migration
yarn db:generate
# 3. Apply it locally
yarn db:migrate
# 4. Commit src/db/schema.ts and the new drizzle/ fileProduction migrations are applied separately from your laptop over Tailscale — see infrastructure/README.md.
Starts the dev Postgres container (if not running), applies migrations,
then launches the bot with nodemon for live-reload. Invite link prints
to the console on first boot.
All commands (under src/commands) are built with the Slash Command interaction.
🪬 IMPORTANT: before developing commands, make sure you invite the bot to your server and that there is a row in the
configstable whosebot_guild_idmatches your guild ID. The bot creates this on first connect; otherwise insert one withyarn db:psql.
Commands under the main folder are registered globally (takes a moment
to propagate). Commands under bots-playground/ are guild-specific and
deploy instantly — use that folder for debugging.
To redeploy commands: yarn slash:dev for dev, yarn slash for production.
yarn test # runs Jest against ./testsTest coverage is light — contributions welcome, especially around command controllers.
Production runs on a single Hetzner VPS as two Docker containers
(db + bot) managed by docker compose. Deploys are manual: SSH to
the box and run ./infrastructure/ops/deploy.sh. Schema migrations
are applied from your laptop over Tailscale.
The full production playbook — host setup, deploy flow, Tailscale configuration, backups, rollback, and the one-time data-migration procedure — lives in infrastructure/README.md.
If you just want to run the bot against your own Postgres:
docker run --env-file .env --name hans -d --restart=always \
en3sis/hans:nightly.env must include a DATABASE_URL reachable from the container. You
are responsible for applying schema migrations against that DB before
the bot connects (DATABASE_URL=... yarn db:migrate).