Skip to content

Latest commit

 

History

381 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


Async - Community
This project is under the GNU GPL v3 license

OmniBot

All the projects in the AsyncCommunityDiscord organisation are used by the discord server discord.gg/graven both by the moderators and the members. Most of the contributors are part of the staff but the members are also allowed to contribute.


What is OmniBot?

OmniBot is a modular Discord bot: each feature is a self-contained module that is auto-discovered at startup and can be installed/uninstalled per Discord server via /modules. Modules declare their own slash commands, event listeners, interaction handlers and a typed configuration schema (edited live with /config <module>) — the core wires everything together, so adding a feature never means touching the bootstrap.

Tech stack

Area Choice
Language TypeScript (ESM, NodeNext), Node.js 24
Discord discord.js v14
Database PostgreSQL 17 via Prisma (multi-file schema)
Package mgr pnpm
Toolchain mise (tool versions), oxlint + oxfmt (lint/format), lefthook (git hooks), commitlint (commit messages)
Tests Vitest
Logging pino
Dev runner pitchfork (one-command stack)

Getting started

Prerequisites

  • mise — pins Node.js, pnpm and pitchfork (mise install). Without mise, install Node.js 24 and pnpm manually (versions are in .mise.toml).
  • Docker (for the PostgreSQL container).
  • A Discord bot token — from the Discord Developer Portal.

Setup

mise install            # installs Node, pnpm, pitchfork from .mise.toml
pnpm install            # installs dependencies and git hooks (lefthook)
cp .env.example .env    # then fill in the values (see Environment below)

Run

One command brings up PostgreSQL and the bot (daemons defined in pitchfork.toml):

pitchfork start bot     # starts the db daemon, waits until it is ready, then runs the bot
pitchfork logs bot      # tail logs   ·   pitchfork stop bot db   # stop everything

Prefer to run things yourself?

docker compose up -d    # PostgreSQL 17
pnpm prisma:migrate     # apply migrations (first run)
pnpm dev                # run the bot with tsx

Environment

Copy .env.example to .env:

  • DISCORD_TOKEN — bot token from the Discord Developer Portal.
  • DATABASE_URL — PostgreSQL connection string (default matches compose.yaml).
  • DEV_GUILD_IDdev only: guild where slash commands are registered instantly (global commands take ~1h to propagate). Required by pnpm dev.

Deployment

compose.prod.yaml deploys the images published to GHCR on each v* tag. It builds nothing, so a host needs Docker, that file and the environment variables — no source checkout, no compiler.

Production runs it as a Portainer stack pointing at this repository, so the compose file in use is always the one on master rather than a copy that drifts from it. Two of its settings matter: Re-pull image, without which a redeploy reuses the image already cached under latest, and the stack environment variables, which take the place of an env file. Cutting a release then deploys itself — the deploy job calls the stack webhook once the images are published.

latest only ever moves to a non-prerelease tag, so cutting a -rc publishes it without deploying it, and the deploy job skips those tags for the same reason.

To run the stack by hand instead:

cp .env.prod.example .env.prod   # then fill in the values
docker compose --env-file .env.prod -f compose.prod.yaml pull
docker compose --env-file .env.prod -f compose.prod.yaml up -d
docker compose --env-file .env.prod -f compose.prod.yaml logs -f bot

Updating is then the same two commands: pull, then up -d — compose recreates only what changed.

The images come from the multi-stage Dockerfile (Node 24 on Alpine, non-root user, tini as PID 1), built by CI on every pull request and published on tag.

Variables to provide (all required, none has a default) — in the Portainer stack configuration, or in .env.prod when running the stack by hand:

  • DISCORD_TOKEN — bot token from the Discord Developer Portal.
  • POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB — database credentials. DATABASE_URL is assembled from them by the compose file; PostgreSQL sits on an internal network and publishes no port.

What the stack does:

  • migrate — a one-shot container that runs prisma migrate deploy before the bot starts (depends_on: service_completed_successfully). The bot image itself ships no Prisma CLI, and two bot instances can never race on the migration history.
  • bot — read-only root filesystem, all capabilities dropped, restarted by Docker (restart: unless-stopped). It has no healthcheck on purpose: the bot exposes no HTTP or TCP endpoint, so any probe would either be a lie about the Discord gateway state or a redundant "is PID 1 alive?" check. tini forwards SIGTERM, which the bot handles by closing the Discord client and the Prisma pool.

Building the image alone:

docker build -t omnibot .

Project layout

src/
├── core/      # framework: module loader, /config & /modules, command/event/interaction loaders
├── modules/   # one folder per feature module (commands/, listeners/, services/, *.config.ts, *.module.ts)
├── lib/       # shared contracts (module, config, registry, listener, …)
└── prisma/    # consolidated schema + migrations (generated — never edit by hand)

Each module is a plugin: drop a folder under src/modules/ and it is discovered automatically — no registration anywhere else.

Common commands

pnpm dev                 # run with tsx
pnpm build               # prisma:generate + tsc → dist/
pnpm test                # run the full test suite (unit + integration)
pnpm format              # oxfmt + oxlint --fix
pnpm prisma:migrate      # create & apply a migration
pnpm prisma:studio       # open Prisma Studio

Contributing

Contributions are welcome — staff and members alike.

  • Start with the developer guide in docs/: how to create a module, slash commands, listeners, interactions, services, the Prisma schema, and the user-facing behaviour.
  • Conventional Commits are enforced (commitlint, via a lefthook commit-msg hook). A lefthook pre-commit hook runs oxfmt + oxlint on staged files — pnpm install sets the hooks up for you.
  • Before opening a PR, make sure pnpm build, pnpm test, pnpm exec oxlint --deny-warnings and pnpm exec oxfmt --check all pass (CI runs the same).
  • Agent instructions live in AGENTS.md (also symlinked as CLAUDE.md) — a concise orientation that is handy for humans too.
  • Known tech-debt and follow-ups are tracked in AUDIT.md.

License

GNU GPL v3.

About

The future async community discord bot, which will handle progressively all our needs

Resources

Stars

3 stars

Watchers

0 watching

Forks

Packages

Used by

Contributors

Languages