A full-featured Discord ticket bot inspired by Ticket Tool, built with discord.js v14 and a self-hosted web dashboard for complete visual configuration.
| Feature | Description |
|---|---|
| Multiple categories | Unlimited ticket categories, each fully independent |
| Custom panel embeds | Title, description, colour, thumbnail, image, footer |
| Category buttons | Up to 25 buttons per panel in any Discord button style |
| Modal questions | Up to 5 custom questions per category (short / paragraph, min/max length) |
| Staff roles | Per-category role access control |
| Mention on open | Ping configured roles when a ticket opens |
| Auto-naming | Flexible channel name tokens: {count}, {username}, {category} |
| Max tickets | Per-category and global user ticket limits |
| Ticket controls | Close (with reason), Claim, Unclaim, Add User, Remove User, Rename |
| HTML Transcripts | Auto-generated on close, sent to log channel |
| DM on close | Optionally DM the user when their ticket is closed |
| Reopen / Delete | Full ticket lifecycle management |
| Staff panel | In-ticket ephemeral menu for all staff actions |
| Page | Description |
|---|---|
| Landing page | Public-facing home page |
| OAuth2 Login | Discord login — no passwords needed |
| Server selection | Shows your guilds where bot is present, with quick invite |
| Overview | Stats, quick actions, recent ticket list |
| Panels | Create, edit, delete panels; live Discord message sync |
| Categories | Full category editor with all settings |
| Ticket Logs | Paginated log with status filter; view individual tickets |
| Settings | Global guild settings (transcript channel, max tickets, DM on close) |
- Node.js ≥ 18
- A MongoDB database (Atlas free tier works)
- A Discord application with a bot token
git clone https://github.com/yourname/tickets-bot
cd tickets-bot
npm installEdit data/settings/config.yml:
BOT_CONFIG:
TOKEN: "your-bot-token"
GUILD_ID: "your-guild-id" # blank = global slash commands
BOT_ID: "your-application-id"
MONGO_URI: "mongodb+srv://..."
EMBED_COLOR: 0x5865F2
DASHBOARD_CONFIG:
PORT: 5555
SECRET: "change-this-secret"
URL: "http://localhost:5555" # public URL for OAuth2 callback
CLIENT_SECRET: "your-discord-client-secret"- Go to discord.com/developers/applications
- Select your application → OAuth2
- Add redirect:
http://localhost:5555/auth/discord/callback - Copy the Client Secret → paste into
config.yml - Enable all required Privileged Gateway Intents (Presence, Server Members, Message Content)
npm startThe bot logs in and the dashboard becomes available at http://localhost:5555.
-
Dashboard → select your server → Categories → New Category
Configure: label, emoji, button style, channels, staff roles, opening embed, questions. -
Dashboard → Panels → New Panel
Configure: embed appearance, select categories. -
In Discord, run:
/ticket setup-panel #channel panel-name: My Panel Name
| Command | Description | Who |
|---|---|---|
/ticket setup-panel <channel> <panel-name> |
Deploy a panel | Admin |
/ticket close [reason] |
Close current ticket | Staff / Owner |
/ticket claim |
Claim ticket | Staff |
/ticket unclaim |
Release claim | Staff |
/ticket add <user> |
Add user to ticket | Staff |
/ticket remove <user> |
Remove user from ticket | Staff |
/ticket rename <name> |
Rename ticket channel | Staff |
/ticket transcript |
Generate HTML transcript | Staff |
/ticket delete |
Permanently delete ticket | Admin |
| Button | Action |
|---|---|
| 🔒 Close | Shows a modal to enter close reason |
| ✋ Claim | Marks the ticket as claimed by you |
| ⚙️ Staff Controls | Opens ephemeral select menu with: Add User, Remove User, Rename, Transcript, Unclaim |
📁 Tickets Bot/
├── index.js Main entry point
├── package.json
│
├── 📁 data/
│ ├── 📁 settings/
│ │ └── config.yml Bot & dashboard configuration
│ └── 📁 models/
│ ├── Guild.js Guild-level settings schema
│ ├── TicketPanel.js Panel configuration schema
│ ├── TicketCategory.js Category configuration schema
│ └── tickets.js Individual ticket records schema
│
├── 📁 commands/
│ ├── 📁 tickets/
│ │ └── ticket.js /ticket command group (9 subcommands)
│ └── 📁 admins/
│ └── timeout.js /timeout command
│
├── 📁 events/
│ ├── clientReady.js Bot ready event
│ ├── interactionCreate.js Slash command dispatcher + autocomplete
│ ├── ticketButtons.js Button & select menu handlers
│ ├── modalsManager.js Modal submit handlers
│ └── ticketCreate.js createTicketChannel() utility
│
├── 📁 handlers/
│ ├── database.js MongoDB connection
│ ├── commands.js Slash command registration
│ └── events.js Event file loader
│
├── 📁 transcripts/ Auto-generated HTML transcripts
│
└── 📁 dashboard/
├── index.js Express app (passport, session, middleware)
├── routes.js All HTTP routes + transcript viewer
├── 📁 views/
│ ├── layout.ejs Master layout (sidebar + topbar)
│ ├── index.ejs Landing page
│ ├── login.ejs Login page
│ ├── error.ejs Error page
│ ├── invite.ejs Bot invite page
│ ├── servers.ejs Server selection
│ ├── 📁 partials/
│ │ ├── header.ejs Shared sidebar & topbar
│ │ ├── footer.ejs Closing HTML
│ │ └── flash.ejs Success/error alerts
│ └── 📁 guild/
│ ├── index.ejs Overview & stats
│ ├── panels.ejs Panel list & deploy
│ ├── panel-edit.ejs Panel editor with live preview
│ ├── categories.ejs Category list
│ ├── category-edit.ejs Full category editor
│ ├── tickets.ejs Ticket log (paginated)
│ ├── ticket-view.ejs Single ticket detail + transcript link
│ └── settings.ejs Guild-wide settings
└── 📁 public/
├── 📁 css/
│ └── style.css Dark theme stylesheet
└── 📁 js/
└── dashboard.js Client-side JS
Use these in the Channel Name Format field of categories or global settings:
| Token | Replaced with |
|---|---|
{count} |
Zero-padded ticket number (e.g. 0042) |
{username} |
Ticket opener's Discord username |
{category} |
Category label (slugified) |
Example: support-{username}-{count} → support-john-0042
PRs are welcome. Open an issue first for large changes.
MIT