Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
f58c142
Remove reason requirement from slash unban command
seeyebe Jun 6, 2025
04b8914
feat: Add custom add_message and remove_message support to role buttons
seeyebe Jun 7, 2025
242015c
fix: handle button interactions properly for slash commands
seeyebe Jun 9, 2025
25771f2
feat: add embed support to welcome messages
seeyebe Jun 11, 2025
863ef3d
Update CaseMsgCmd.ts
seeyebe Jul 8, 2025
f9c566b
Update actualCaseCmd.ts
seeyebe Jul 8, 2025
ea59b88
feat: add reply info in message delete log
seeyebe Sep 26, 2025
b467c39
feat: add has_attachments trigger
seeyebe Sep 26, 2025
a3242ec
fix: circular reference
Dragory Nov 4, 2025
00b41c5
feat: automod dry run; automod debug return object
Dragory Nov 4, 2025
ceb7a38
feat: debug_automod command
Dragory Nov 4, 2025
e4b6057
feat: add util fns to fetch users/members without duplicated requests
Dragory Nov 4, 2025
1477688
feat: fetch unknown user/member in automod
Dragory Nov 4, 2025
9dbe4b7
chore: reduce log spam
Dragory Nov 4, 2025
515a068
chore: don't log plugin load errors on first load
Dragory Nov 4, 2025
82d4eca
feat: instructions and restrictions for AI tooling
Dragory Nov 4, 2025
3372ac2
feat: add basic cf tunnel support for devenv
Dragory Nov 4, 2025
8b6cf4b
chore: run formatter
almeidx Jun 1, 2025
886b79b
fix: package-lock newlines
Dragory Nov 8, 2025
752a138
Merge pull request #512 from ZeppelinBot/chore/format
Dragory Nov 9, 2025
cee4e20
build(deps): bump the github-actions group with 2 updates (#513)
dependabot[bot] Nov 9, 2025
2be9f30
dev: update devenv to Node 24; add pnpm
Dragory Nov 10, 2025
677e24a
refactor: replace npm workspace with pnpm monorepo
Dragory Nov 10, 2025
a36f4f1
feat: update TS to 5.9.3, Knub to 32.0.0-next.27, djs to 14.23.2
Dragory Nov 10, 2025
b9f3c2a
chore: normalize zod imports
Dragory Nov 10, 2025
9ec63fa
chore: update to knub@32.0.0-next.28
Dragory Nov 10, 2025
d77a509
fix: type errors
Dragory Nov 10, 2025
47e948c
feat: use pnpm in codequality workflow
Dragory Nov 10, 2025
a8f2c7f
chore: update .nvmrc
Dragory Nov 10, 2025
d848c8b
build: use pnpm in Dockerfile; node 24
Dragory Nov 10, 2025
2f3c3ad
chore: set package.json engines to 24+
Dragory Nov 10, 2025
ebe8dff
chore: npm -> pnpm
Dragory Nov 10, 2025
1694309
chore: mark pnpm-lock.yaml as binary for git
Dragory Nov 11, 2025
c2f05ad
feat: add CommandAliases plugin
seeyebe Nov 11, 2025
6aa7982
chore: remove leftover comment in DispatchAliasEvt
seeyebe Nov 11, 2025
9665704
chore: fix missing comma in CommandAliases plugin docs
seeyebe Nov 11, 2025
1fc524b
chore: knub -> vety
Dragory Nov 13, 2025
30548b8
Merge branch 'master' into feat/commands-aliases
seeyebe Nov 13, 2025
73b0e29
fix: typeorm commands
Dragory Nov 13, 2025
cec1bf3
feat: redis support
Dragory Nov 13, 2025
9c3ac7f
chore: npm -> pnpm in start-dev.js
Dragory Nov 13, 2025
a1000ce
chore: add warning traces for dev
Dragory Nov 13, 2025
c58ac36
chore: ready -> clientReady
Dragory Nov 13, 2025
50376b1
fix: FishFish session token expiry time calculation
Dragory Nov 13, 2025
3fd88b6
fix: disable non-functional FishFish websocket connection
Dragory Nov 13, 2025
5d864e8
feat: debug counters
Dragory Nov 13, 2025
b802e4d
feat(getOrFetchUser): redis cache; use debug counters
Dragory Nov 13, 2025
2ad152d
refactor(resolveUser): use getOrFetchUser internally; debug counters
Dragory Nov 13, 2025
ee57e6c
chore: update typeorm
Dragory Nov 13, 2025
80d1719
chore: remove debug log
Dragory Nov 13, 2025
6ae477b
chore: update tmp package
Dragory Nov 13, 2025
e0bc978
chore: remove unnecessary @types/moment-timezone package
Dragory Nov 13, 2025
f5472d4
chore: remove clinic package
Dragory Nov 13, 2025
6f2b027
chore: remove unused rimraf package
Dragory Nov 13, 2025
5ba4053
chore: update multer package
Dragory Nov 13, 2025
8b7d656
refactor: change import from 'knub' to 'vety'
seeyebe Nov 13, 2025
78f3d50
refactor: change import from 'knub' to 'vety'
seeyebe Nov 13, 2025
eb1fa28
debug: resolveUser context
Dragory Nov 13, 2025
8cf91c1
Merge pull request #570 from seeyebe/feat/commands-aliases
Dragory Nov 13, 2025
bc42741
refactor: change import from 'zod/v4' to 'zod'
seeyebe Nov 13, 2025
02d3c3e
Merge pull request #560 from seeyebe/feat/has_attachment-trigger
Dragory Nov 13, 2025
6cdc263
fix: getKnubInstance -> getVetyInstance
Dragory Nov 13, 2025
63510a5
fix: initial load plugin error ignoring
Dragory Nov 13, 2025
0ac6928
refactor(automod): replace "has" with "min_count"/"max_count" in has_…
Dragory Nov 13, 2025
cee5623
fix: migration commands
Dragory Nov 13, 2025
d2c73fd
chore: ignore pnpm store + other files from docker
Dragory Nov 13, 2025
79c5438
docs: update update docs
Dragory Nov 13, 2025
1ecc163
chore: replace console.log() with debug counters
Dragory Nov 13, 2025
175d266
debug: add debug counter for runAutomodOnMessage
Dragory Nov 13, 2025
4e23036
fix: !cases not working with user id
Dragory Nov 13, 2025
82087ea
fix: catch reaction invalid emoji errors
Dragory Nov 13, 2025
f4764d3
Merge branch 'master' into feat/welcome-message-embed-support
seeyebe Nov 14, 2025
67a3e2a
Merge branch 'master' into fix/button-interaction-acknowledgment
seeyebe Nov 14, 2025
8c49fa7
Merge branch 'master' into feat/role-buttons-custom-messages
seeyebe Nov 14, 2025
f7a20fb
Merge branch 'master' into fix/slash-unban-reason-validation
seeyebe Nov 14, 2025
8039edf
Merge branch 'master' into feat/log-replied-message
seeyebe Nov 14, 2025
1a61bc0
Merge branch 'master' into fix/remove-prefix-case-show-flag
seeyebe Nov 14, 2025
6959d8a
fix: crash when cleaning 0 messages
Dragory Nov 15, 2025
ec09c17
feat: bake build info into image
Dragory Nov 15, 2025
26bbe55
chore: remove unused packages
Dragory Nov 15, 2025
13fbddd
Merge pull request #559 from seeyebe/feat/log-replied-message
Dragory Nov 15, 2025
8f883e5
Merge pull request #549 from seeyebe/fix/remove-prefix-case-show-flag
Dragory Nov 15, 2025
07401ba
Merge pull request #532 from seeyebe/feat/role-buttons-custom-messages
Dragory Nov 15, 2025
e311279
Merge pull request #531 from seeyebe/fix/slash-unban-reason-validation
Dragory Nov 15, 2025
81db425
fix: knub -> vety
Dragory Nov 15, 2025
5227e6c
fix: missing import
Dragory Nov 15, 2025
7218fbd
chore: align welcome DMs with MessageContent helper
seeyebe Nov 15, 2025
0ff6d91
Merge pull request #540 from seeyebe/feat/welcome-message-embed-support
Dragory Nov 15, 2025
942b8d0
Merge pull request #537 from seeyebe/fix/button-interaction-acknowled…
Dragory Nov 15, 2025
ec57153
fix: update imports for Logs plugin to use correct modules
seeyebe Nov 15, 2025
77e356b
Merge pull request #573 from seeyebe/fix/fixes
Dragory Nov 15, 2025
6795bf7
Merge branch 'master' of github.com:ZeppelinBot/Zeppelin
Dragory Nov 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
93 changes: 93 additions & 0 deletions .cursorignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.clinic
.clinic-bot
.clinic-api

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
*.env
.env

# windows folder options
desktop.ini

# PHPStorm
.idea/

# Misc
/convert.js
/startscript.js
.cache
npm-ls.txt
npm-audit.txt
.vscode/launch.json

# Debug files
*.debug.ts
*.debug.js

.vscode/

config-errors.txt
/config-schema.json

*.tsbuildinfo

# Legacy data folders
/docker/development/data
/docker/production/data
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

**/node_modules
**/dist
**/.pnpm-store
**/.docker

**/*.log
**/npm-debug.log*
Expand Down Expand Up @@ -35,6 +37,7 @@
# Debug files
**/*.debug.ts
**/*.debug.js
/debug

**/.vscode

Expand Down
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ DEVELOPMENT_SSH_PASSWORD=password
# If your user has a different UID than 1000, you might have to fill that in here to avoid permission issues
#DEVELOPMENT_UID=1000

# If using the cftunnel docker compose profile, set your Cloudflare Tunnel token here
#CF_TUNNEL_TOKEN=


# ==========================
# PRODUCTION - STANDALONE
Expand Down
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
package-lock.json binary
pnpm-lock.yaml binary
26 changes: 13 additions & 13 deletions .github/workflows/codequality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [22]

steps:
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8

- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903
with:
node-version: ${{ matrix.node-version }}
- name: npm install, lint, code style check
run: |
npm ci
npm run lint
npm run codestyle-check
node-version: 24

- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
with:
version: 10.19.0
run_install: true

- run: |
pnpm run lint
pnpm run codestyle-check
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ build/Release
# Dependency directories
node_modules/
jspm_packages/
.pnpm-store

# Typescript v1 declaration files
typings/
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
24
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The project is called Zeppelin. It's a Discord bot that uses Discord.js. The bot is built on the Vety framework (formerly called Knub).

This repository is a monorepository that contains these projects:
1. **Backend**: The shared codebase of the bot and API. Located in `backend`.
2. **Dashboard**: The web dashboard that contains the bot's management interface and documentation. Located in `dashboard`.
3. **Config checker**: A tool to check the configuration of the bot. Located in `config-checker`.

There is also a `shared` folder that contains shared code used by all projects, such as types and utilities.

# Backend
The backend codebase is located in the `backend` directory. It contains the main bot code, API code, and shared code used by both the bot and API.
Zeppelin's functionality is split into plugins, which are located in the `src/plugins` directory.
Each plugin has its own directory, with a `types.ts` for config types, `docs.ts` for a `ZeppelinPluginDocs` structure, and the plugin's main file.
Each plugin has an internal name, such as "common". In this example, the folder would be `src/plugins/Common` (note the capitalization). The plugin's main file would be `src/plugins/CommonPlugin.ts`.
There are two types of plugins: "guild plugins" and "global plugins". Guild plugins are loaded on a per-guild basis, while global plugins are loaded once for the entire bot.
Plugins can specify dependencies on other plugins and call their public methods. Likewise, plugins can specify public methods in the main file.
Available plugins are specified in `src/plugins/availablePlugins.ts`.

Zeppelin's data layer uses TypeORM. Entities are located in `src/data/entities`, while repositories are in `src/data`. If the repository name is prefixed with "Guild", it's a guild-specific repository. If it's prefixed with "User", it's a user-specific repository. If it has no prefix, it's a global repository.

Environment variables are parsed in `src/env.ts`.
32 changes: 24 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
FROM node:22 AS build
FROM node:24 AS build

ARG COMMIT_HASH
ARG BUILD_TIME

RUN mkdir /zeppelin
RUN chown node:node /zeppelin

# Install pnpm
RUN npm install -g pnpm@10.19.0

USER node

# Install dependencies before copying over any other files
COPY --chown=node:node package.json package-lock.json /zeppelin
COPY --chown=node:node package.json pnpm-workspace.yaml pnpm-lock.yaml /zeppelin
RUN mkdir /zeppelin/backend
COPY --chown=node:node backend/package.json /zeppelin/backend
RUN mkdir /zeppelin/shared
Expand All @@ -15,23 +21,33 @@ RUN mkdir /zeppelin/dashboard
COPY --chown=node:node dashboard/package.json /zeppelin/dashboard

WORKDIR /zeppelin
RUN npm ci
RUN CI=true pnpm install

COPY --chown=node:node . /zeppelin

# Build backend
WORKDIR /zeppelin/backend
RUN npm run build
RUN pnpm run build

# Build dashboard
WORKDIR /zeppelin/dashboard
RUN npm run build
RUN pnpm run build

# Prune dev dependencies
# Only keep prod dependencies
WORKDIR /zeppelin
RUN npm prune --omit=dev
RUN CI=true pnpm install --prod

# Add version info
RUN echo "${COMMIT_HASH}" > /zeppelin/.commit-hash
RUN echo "${BUILD_TIME}" > /zeppelin/.build-time

# --- Main image ---

FROM node:22-alpine AS main
FROM node:24-alpine AS main

RUN npm install -g pnpm@10.19.0

USER node
COPY --from=build --chown=node:node /zeppelin /zeppelin

WORKDIR /zeppelin
53 changes: 22 additions & 31 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,78 +11,71 @@
"watch": "tsc-watch --build --onSuccess \"node start-dev.js\"",
"watch-yaml-parse-test": "tsc-watch --build --onSuccess \"node dist/yamlParseTest.js\"",
"build": "tsc --build",
"start-bot-dev": "node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/index.js",
"start-bot-dev-debug": "DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9229 dist/index.js",
"start-bot-prod": "node --enable-source-maps --stack-trace-limit=30 dist/index.js",
"start-bot-prod-debug": "DEBUG=true clinic heapprofiler --collect-only --dest .clinic-bot -- node --enable-source-maps --stack-trace-limit=30 dist/index.js",
"watch-bot": "tsc-watch --build --onSuccess \"npm run start-bot-dev\"",
"typecheck": "tsc --noEmit",
"start-bot-dev": "node --enable-source-maps --stack-trace-limit=30 --trace-warnings --inspect=0.0.0.0:9229 dist/index.js",
"start-bot-prod": "node --enable-source-maps --stack-trace-limit=30 --trace-warnings dist/index.js",
"watch-bot": "tsc-watch --build --onSuccess \"pnpm run start-bot-dev\"",
"start-api-dev": "node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/api/index.js",
"start-api-dev-debug": "DEBUG=true clinic heapprofiler --collect-only --dest .clinic-api -- node --enable-source-maps --stack-trace-limit=30 --inspect=0.0.0.0:9239 dist/api/index.js",
"start-api-prod": "node --enable-source-maps --stack-trace-limit=30 dist/api/index.js",
"start-api-prod-debug": "clinic heapprofiler --collect-only --dest .clinic-api -- node --enable-source-maps --stack-trace-limit=30 dist/api/index.js",
"watch-api": "tsc-watch --build --onSuccess \"npm run start-api-dev\"",
"typeorm": "node ../node_modules/typeorm/cli.js",
"migrate": "npm run typeorm -- migration:run -d dist/data/dataSource.js",
"migrate-prod": "npm run migrate",
"migrate-dev": "npm run build && npm run migrate",
"migrate-rollback": "npm run typeorm -- migration:revert -d dist/data/dataSource.js",
"migrate-rollback-prod": "npm run migrate-rollback",
"migrate-rollback-dev": "npm run build && npm run migrate-rollback",
"watch-api": "tsc-watch --build --onSuccess \"pnpm run start-api-dev\"",
"migrate": "pnpm exec typeorm migration:run -d dist/data/dataSource.js",
"migrate-prod": "pnpm run migrate",
"migrate-dev": "pnpm run build && pnpm run migrate",
"migrate-rollback": "pnpm exec typeorm migration:revert -d dist/data/dataSource.js",
"migrate-rollback-prod": "pnpm run migrate-rollback",
"migrate-rollback-dev": "pnpm run build && pnpm run migrate-rollback",
"validate-active-configs": "node --enable-source-maps dist/validateActiveConfigs.js > ../config-errors.txt",
"export-config-json-schema": "node --enable-source-maps dist/exportSchemas.js ../config-checker/public/config-schema.json",
"test": "npm run build && npm run run-tests",
"test": "pnpm run build && pnpm run run-tests",
"run-tests": "ava",
"test-watch": "tsc-watch --build --onSuccess \"npx ava\""
"test-watch": "tsc-watch --build --onSuccess \"pnpm exec ava\""
},
"dependencies": {
"@silvia-odwyer/photon-node": "^0.3.1",
"@zeppelinbot/shared": "workspace:*",
"bufferutil": "^4.0.3",
"clinic": "^13.0.0",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"deep-diff": "^1.0.2",
"discord.js": "^14.19.3",
"discord.js": "*",
"emoji-regex": "^8.0.0",
"escape-string-regexp": "^1.0.5",
"express": "^4.20.0",
"fp-ts": "^2.0.1",
"humanize-duration": "^3.15.0",
"js-yaml": "^4.1.0",
"knub": "^32.0.0-next.25",
"knub-command-manager": "^9.1.0",
"last-commit-log": "^2.1.0",
"lodash-es": "^4.17.21",
"moment-timezone": "^0.5.21",
"multer": "^1.4.5-lts.1",
"multer": "^2.0.2",
"mysql2": "^3.9.8",
"parse-color": "^1.0.0",
"passport": "^0.6.0",
"passport-custom": "^1.0.5",
"passport-oauth2": "^1.6.1",
"pkg-up": "^3.1.0",
"redis": "^5.9.0",
"reflect-metadata": "^0.1.12",
"regexp-worker": "^1.1.0",
"safe-regex": "^2.0.2",
"seedrandom": "^3.0.1",
"strip-combining-marks": "^1.0.0",
"threads": "^1.7.0",
"tlds": "^1.221.1",
"tmp": "0.0.33",
"tmp": "0.2.5",
"tsconfig-paths": "^3.9.0",
"twemoji": "^12.1.4",
"typeorm": "^0.3.17",
"typeorm": "^0.3.27",
"utf-8-validate": "^5.0.5",
"uuid": "^9.0.0",
"yawn-yaml": "github:dragory/yawn-yaml#string-number-fix-build",
"zod": "^3.25.17"
"vety": "1.0.0-rc2",
"zod": "^4.1.12"
},
"devDependencies": {
"@types/cors": "^2.8.5",
"@types/express": "^4.16.1",
"@types/jest": "^24.0.15",
"@types/js-yaml": "^3.12.1",
"@types/lodash-es": "^4.17.12",
"@types/moment-timezone": "^0.5.6",
"@types/multer": "^1.4.7",
"@types/passport": "^1.0.0",
"@types/passport-oauth2": "^1.4.8",
Expand All @@ -92,9 +85,7 @@
"@types/twemoji": "^12.1.0",
"@types/uuid": "^9.0.2",
"ava": "^5.3.1",
"rimraf": "^2.6.2",
"source-map-support": "^0.5.16",
"zod-to-json-schema": "^3.22.3"
"source-map-support": "^0.5.16"
},
"ava": {
"files": [
Expand Down
7 changes: 5 additions & 2 deletions backend/src/RegExpRunner.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CooldownManager } from "knub";
import { CooldownManager } from "vety";
import { EventEmitter } from "node:events";
import { RegExpWorker, TimeoutError } from "regexp-worker";
import { MINUTES, SECONDS } from "./utils.js";
Expand All @@ -9,7 +9,10 @@ const isTimeoutError = (a): a is TimeoutError => {
};

export class RegExpTimeoutError extends Error {
constructor(message: string, public elapsedTimeMs: number) {
constructor(
message: string,
public elapsedTimeMs: number,
) {
super(message);
}
}
Expand Down
2 changes: 1 addition & 1 deletion backend/src/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express, { Request, Response } from "express";
import https from "https";
import pick from "lodash/pick.js";
import { pick } from "lodash-es";
import passport from "passport";
import { Strategy as CustomStrategy } from "passport-custom";
import OAuth2Strategy from "passport-oauth2";
Expand Down
4 changes: 2 additions & 2 deletions backend/src/api/docs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import express from "express";
import z from "zod/v4";
import { z } from "zod";
import { $ZodPipeDef } from "zod/v4/core";
import { availableGuildPlugins } from "../plugins/availablePlugins.js";
import { ZeppelinGuildPluginInfo } from "../types.js";
import { indentLines } from "../utils.js";
import { notFound } from "./responses.js";
import { $ZodPipeDef } from "zod/v4/core";

function isZodObject(schema: z.ZodType): schema is z.ZodObject<any> {
return schema.def.type === "object";
Expand Down
Loading
Loading