Skip to main content

Getting Started

Discord Ticket Bot

A modern, self-hosted Discord ticket bot built on Discord.js v14 and SQLite — no external database, no telemetry, full feature set out of the box.

License: AGPL-3.0 Node.js Discord.js Documentation


✨ Features

FeatureDescription
🎫 Ticket TypesUp to 25 configurable types with individual emoji, color, category & questions
📋 QuestionnairesModal forms (up to 5 questions) shown when opening a ticket
🙋 Claim SystemStaff can claim/unclaim — button toggles, embed & topic update automatically
🔴 PrioritiesLow / Medium / High / Urgent via /priority — shown in channel topic & embed
📝 Staff NotesPrivate notes via /note add / /note list
🔀 Move TicketMove to a different type/category via /move or button (staff only)
🛡️ Type-specific Staff RolesEach ticket type can define its own staff roles
🖼️ Panel Logo & BannerOptional logo thumbnail and/or banner image in the panel embed
🎛️ Panel Interaction TypeChoose between a Button or a direct Select Menu in the panel
⭐ Rating System1–5 star feedback after closing, automatically posted to a configured channel
⏰ Staff ReminderAutomatic ping inside the ticket if no staff responds within X hours
⏰ Auto-CloseAutomatically close inactive tickets with a configurable warning period
🔗 Transcript LinksTranscripts stored online and accessible via a public link
📄 HTML TranscriptFull self-contained HTML transcript — avatars embedded as Base64, no CDN required
🌐 Custom DomainPremium users can serve transcripts under their own domain
📊 StatisticsServer-wide stats and detailed per-user stats via /stats
🚫 Blacklist/blacklist add/remove/list to block users from opening tickets
💬 Canned ResponsesPre-defined snippets sent with one command — configured in snippets.jsonc
🔒 Ticket LockLock/unlock a ticket to prevent the user from sending messages
📢 BroadcastSend a message to all open ticket channels at once
🔔 User NotificationsOptional DM notification for users when a staff member replies
🎮 Dynamic Bot StatusAutomatically display the number of open tickets in the bot status
🌍 MultilingualGerman and English included, easily extensible
🗄️ SQLiteNo external database required — file is created automatically
🔄 Auto-Update CheckChecks for new GitHub releases on startup and notifies with update instructions

🔗 MSK Transcript Service

Instead of sending transcripts as file attachments via DM, the bot can upload them to www.msk-scripts.de and generate a public link — accessible in any browser, no download required.

Subscription Tiers

FeatureBasic (free)Premium (€5/mo)Premium+ (€10/mo)
Transcript as link
Max. transcript size10 MB100 MB250 MB
File attachments in transcript
Max. attachment size per ticket150 MB500 MB
Custom domain
Storage duration30 days60 days90 days

Premium and Premium+ are unlocked via GitHub Sponsors.

Getting your API Key

  1. Visit www.msk-scripts.de/verify
  2. Sign in with your GitHub account
  3. Connect your Discord account
  4. Select your server → your API key is generated instantly

Then add it to your .env:

MSK_API_KEY='your_api_key_here'
MSK_API_URL="https://www.msk-scripts.de"

Custom Domain (Premium & Premium+)

Premium users can serve transcripts under their own domain (e.g. tickets.yourserver.com).

  1. Visit www.msk-scripts.de/dashboard after verifying
  2. Enter your domain and set a DNS A-Record pointing to the server IP shown
  3. Click "Check DNS" once propagation is complete — SSL is set up automatically

📖 Full setup guide: docu.msk-scripts.de


📁 Project Structure

discord_ticketbot/
├── index.js # Entry point
├── package.json
├── .env.example # Environment variable template
├── ticketbot.service # systemd unit file for Linux servers
├── assets/ # Static files (logo, banner images)
│ ├── logo.png # Panel logo thumbnail (place your own here)
│ └── banner.png # Panel banner image (place your own here)
├── config/
│ ├── config.example.jsonc # Configuration template (with comments)
│ └── snippets.example.jsonc # Canned responses template
├── docs/
│ ├── setup-en.md # MSK Transcript Service setup guide (English)
│ └── setup-de.md # MSK Transcript Service setup guide (German)
├── locales/
│ ├── de.json # German
│ └── en.json # English
├── data/
│ └── tickets.db # SQLite database (auto-created)
└── src/
├── client.js # Extended Discord Client
├── config.js # Config loader & validation
├── database.js # All DB operations (SQLite)
├── handlers/
│ ├── commandHandler.js # Loads & registers slash commands
│ ├── eventHandler.js # Loads Discord events
│ └── componentHandler.js # Loads buttons, modals, menus
├── commands/ # Slash commands
│ ├── setup.js # /setup – Send panel
│ ├── close.js # /close – Close ticket
│ ├── add.js # /add – Add user
│ ├── remove.js # /remove – Remove user
│ ├── claim.js # /claim – Claim ticket
│ ├── unclaim.js # /unclaim – Unclaim ticket
│ ├── move.js # /move – Move ticket
│ ├── rename.js # /rename – Rename channel
│ ├── transcript.js # /transcript – Generate HTML transcript
│ ├── priority.js # /priority – Set priority (topic + embed)
│ ├── note.js # /note – Staff notes
│ ├── blacklist.js # /blacklist – Block users
│ ├── stats.js # /stats – Statistics (server & user)
│ ├── snippet.js # /snippet – Send canned responses
│ ├── broadcast.js # /broadcast – Send to all open tickets
│ └── lock.js # /lock – Lock/unlock ticket
├── events/
│ ├── ready.js # Bot start, status, auto-close & staff reminder loop
│ ├── messageCreate.js # Activity tracking + DM notifications
│ └── interactionCreate.js # Route all interactions
├── components/
│ ├── buttons/
│ │ ├── openTicket.js # tb_open
│ │ ├── closeTicket.js # tb_close
│ │ ├── claimTicket.js # tb_claim
│ │ ├── unclaimTicket.js # tb_unclaim
│ │ ├── moveTicket.js # tb_move
│ │ ├── deleteTicket.js # tb_delete
│ │ ├── deleteConfirm.js # tb_deleteConfirm
│ │ ├── deleteCancel.js # tb_deleteCancel
│ │ ├── rateTicket.js # tb_rate:N
│ │ └── notifyToggle.js # tb_notifyToggle
│ ├── modals/
│ │ ├── closeReason.js # tb_modalClose
│ │ └── ticketQuestions.js # tb_modalQuestions:type
│ └── menus/
│ ├── panelSelect.js # tb_panelSelect
│ ├── ticketType.js # tb_selectType
│ └── moveSelect.js # tb_moveSelect
└── utils/
├── logger.js # Coloured console logger
├── embeds.js # All embed constructors
├── transcript.js # Self-contained HTML (avatars embedded as Base64)
├── mskApi.js # MSK Transcript Service API client
├── ticketActions.js # Core logic: openTicket, performClose, performMove
├── versionCheck.js # Startup update check against GitHub releases
└── snippets.js # Snippet loader & placeholder engine

⚙️ Slash Commands

CommandPermissionDescription
/setupAdministratorSend the ticket panel
/close [reason]ConfigurableClose the current ticket
/claimStaffClaim a ticket — updates topic & embed, button toggles to Unclaim
/unclaimStaffRelease a claimed ticket — updates topic & embed, button toggles back
/moveStaffMove ticket to a different type/category
/add <user>StaffAdd a user to the ticket
/remove <user>StaffRemove a user from the ticket
/rename <name>StaffRename the ticket channel
/transcriptStaffGenerate an HTML transcript
/priority <level>StaffSet ticket priority (updates channel topic & embed)
/note add <text>StaffAdd a staff note
/note listStaffList all notes for this ticket
/statsStaffServer-wide ticket statistics
/stats @userStaffDetailed statistics for a specific user
/blacklist addManage GuildBlock a user
/blacklist removeManage GuildUnblock a user
/blacklist listManage GuildShow the blacklist
/snippet send <name>StaffSend a canned response into the ticket
/snippet listStaffShow all available snippets
/lock lock [reason]StaffLock ticket — user cannot send messages
/lock unlockStaffUnlock ticket — restore user message access
/broadcast <message>StaffSend a message to all open ticket channels

🔘 Ticket Buttons

Every ticket channel contains a button row at the top:

ButtonVisible whenDescription
🔒 Close TicketAlways (configurable)Disables all buttons, generates transcript, closes & renames channel
🙋 ClaimclaimButton: true, not yet claimedStaff claims — topic & embed update, button becomes Unclaim
🙌 UnclaimclaimButton: true, already claimedStaff releases — topic & embed update, button becomes Claim
🔀 MoveMore than 1 ticket type configuredStaff opens type selection (staff only)
🗑️ Delete TicketAfter closingDeletes the channel after confirmation
🔕 Notify meuserNotifications.enabled: trueUser opts in to DM notifications when a staff member replies

🗄️ Database Schema

The SQLite database is created automatically at data/tickets.db. Columns are added automatically via migration if they are missing.

TableContents
ticketsAll tickets: status, type, priority, claim, lock, notify, reminder, transcript
blacklistBlocked users with reason and timestamp
staff_notesPrivate staff notes per ticket
ratingsRatings (1–5 ⭐) with optional comment

Columns added in recent updates:

ColumnDefaultPurpose
locked0Whether the ticket is currently locked
notify_on_reply0Whether the creator opted in to DM notifications
last_notify_sentNULLTimestamp of the last notification DM (30-min cooldown)

🌍 Adding a New Language

  1. Copy locales/de.json, e.g. as locales/fr.json
  2. Translate all strings
  3. Set "lang": "fr" in config/config.jsonc

📝 License

AGPL-3.0 — Source code must remain open and be published under the same license when distributed or hosted.

Forks and modifications that remove or bypass the MSK Transcript Service integration are not permitted.