Skip to main content

Configuration

๐Ÿ› ๏ธ Configuration Referenceโ€‹

Startup Log Visibilityโ€‹

"showLog": true // Show INFO log messages on startup (commands, events, components)
// Set to false for a cleaner output in production

Panel Interaction Typeโ€‹

"panel": {
"interactionType": "BUTTON" // "BUTTON" (default) or "SELECT_MENU"
}
ModeBehaviour
"BUTTON"A green button is shown. Clicking it opens an ephemeral select menu, always fresh, no Discord caching issue.
"SELECT_MENU"The select menu is shown directly in the panel. After every use it automatically resets, so users never need to restart Discord to open a second ticket of the same type.

Panel Logo & Bannerโ€‹

"panel": {
"logo": { "enabled": true, "file": "logo.png" },
"banner": { "enabled": true, "file": "banner.png" }
}

Supported formats: PNG, JPG, GIF, WEBP. Run /setup again after adding or changing images.

Channel State Overviewโ€‹

StateChannel NameChannel TopicOpening Embed
Ticket openedticket-username๐ŸŸก MediumPriority: ๐ŸŸก Medium
/priority urgentticket-username๐Ÿ”ด UrgentPriority: ๐Ÿ”ด Urgent
/claimticket-username๐ŸŸก Medium | ๐Ÿ™‹ Claimed by @Staff+ Claimed by field
/unclaimticket-username๐ŸŸก Mediumfield removed
/lock lockticket-usernameunchangedlock notice posted
Ticket closedclosed-ticket-usernameunchangedall buttons removed
Reopenticket-usernamerestoredreopen embed + ticket buttons restored

Note on rate-limits: Discord limits channel topic changes to 2 per 10 minutes. A warning is shown in the ticket and the update appears automatically once the limit resets.

Ticket Typesโ€‹

{
"codeName": "support",
"name": "Support",
"description": "...",
"emoji": "๐Ÿ’ก",
"color": "#ff0000", // Hex color or "" to use mainColor
"categoryId": "123456789",
"priority": "medium", // Predefined start priority: "low", "medium", "high" or "urgent" (defaults to "medium")
"ticketNameOption": "", // USERNAME, USERID, TICKETCOUNT or ""
"customDescription": "...", // Variables: REASON1, REASON2, USERNAME, USERID
"cantAccess": ["roleId"],
"requiredRoles": [], // Empty = everyone, otherwise one of these roles is needed
"staffRoles": [], // Type-specific staff roles, empty = global staff roles
"askQuestions": true,
"questions": [
{ "label": "Question", "placeholder": "...", "style": "SHORT", "maxLength": 500 }
]
}

staffRoles replaces the global roles. A type with its own staffRoles is handled by those roles only: they see the channel, can claim, lock, move and close, and the staff reminder pings them. The global rolesWhoHaveAccessToTheTickets do not act on such a ticket. If the global staff role should keep access, add it to the type's staffRoles. Administrators always have access.

requiredRoles limits who can open a type. Empty (the default) means everyone may open it. With roles listed, a member needs at least one of them, otherwise the bot refuses with "This category is only open to members with a specific role." Use it for a category like script support that only sponsors may open, while order or invoice types stay open to all. cantAccess still applies on top and wins: a member with a blocked role cannot open the type even if they hold a required role. There is no exception for staff or administrators, so give your team one of the roles if they should be able to open the type themselves. With the BUTTON panel, the type list a member sees only offers the types they may open; if just one is left it opens directly. The SELECT_MENU panel is the same for everyone, so there the refusal message is shown after picking.

Open Restrictionsโ€‹

"rolesWhoCanNotCreateTickets": [], // roles that cannot open any ticket
"maxTicketOpened": 2 // open tickets per user, 0 = unlimited

Together with cantAccess and requiredRoles per type and the /blacklist, these are checked on every way a ticket can be opened, and the open limit cannot be bypassed by clicking quickly several times.

Bot Statusโ€‹

"status": {
"enabled": true,
"dynamic": false, // true = live ticket count in status
"dynamicText": "๐ŸŽซ {open} open tickets", // placeholders: {open}, {total}, {closed}
"dynamicInterval": 5, // update interval in minutes
"text": "Support Tickets", // used when dynamic: false
"type": "WATCHING", // PLAYING, WATCHING, LISTENING, STREAMING, COMPETING
"status": "online"
}

User Notificationsโ€‹

"userNotifications": {
"enabled": true // Show a ๐Ÿ”• "Notify me" button in new tickets.
// User opts in โ†’ receives a DM when staff first replies.
// Rate-limited to 1 DM per 30 minutes per ticket.
}

Canned Responses (Snippets)โ€‹

Snippets are defined in a separate file, not in config.jsonc:

cp config/snippets.example.jsonc config/snippets.jsonc
{
"snippets": [
{
"name": "welcome",
"description": "Welcome message at the start of a ticket",
"content": "Hey {user}! ๐Ÿ‘‹ Thanks for opening a ticket. We'll be with you shortly.",
"embed": {
"title": "๐Ÿ‘‹ Welcome",
"color": "#5865F2"
}
},
{
"name": "docs",
"description": "Link to the MSK-Scripts documentation",
"content": "Hey {user}, check out our docs: https://docu.msk-scripts.de",
"embed": null
}
]
}

Available placeholders: {user} ยท {staff} ยท {type} ยท {priority}

Commands: /snippet send <name> ยท /snippet list

Snippets support autocomplete, start typing the name or description to filter.

Staff Reminderโ€‹

"staffReminder": {
"enabled": true,
"afterHours": 4,
"pingRoles": true
}

The bot checks all open tickets every 15 minutes. Each ticket is only reminded once.

Closingโ€‹

"closeOption": {
"closeButton": true,
"dmUser": true, // send the creator a DM when the ticket closes
"createTranscript": true,
"transcriptToUser": true, // false = transcript only in the log channel, not in the DM
"askReason": true,
"whoCanCloseTicket": "EVERYONE", // or "STAFFONLY"
"closeTicketCategoryId": "CATEGORY_ID_HERE"
}

Without an MSK API key the transcript is a self-contained HTML file with avatars, custom emojis and images (up to 5 MB) embedded. With a key it is uploaded and shared as a link, see Service Setup.

Rating Systemโ€‹

"ratingSystem": {
"enabled": true,
"dmUser": true, // ask in the DM instead of the channel
"ratingsChannelId": "CHANNEL_ID_HERE",
"commentRoles": [] // who may use "Comment Feedback"; empty = staff
}

Only the ticket creator can rate. The post shows the ticket category and the user's avatar. Staff reply to it via right click โ†’ Apps โ†’ Comment Feedback; the comment is added to the embed, and a second comment by the same person replaces the first.

Auto-Closeโ€‹

"autoClose": {
"enabled": true,
"inactiveHours": 48,
"warnBeforeHours": 6,
"excludeClaimed": true
}

/autoclose pause excludes a single ticket from the warning, the auto-close and the staff reminder. /autoclose resume starts a fresh inactivity window.

Update Notificationโ€‹

"updateNotification": {
"enabled": true, // default on, also when the block is missing
"intervalHours": 1 // minimum 0.25
}

Posts an embed to logsChannelId when a newer release is available on GitHub. Each version is announced once, also across restarts. Without logsChannelId the check does not run.

Reopenโ€‹

Closed tickets can be reopened via a โ™ป๏ธ Reopen button on the closed-ticket message and the /reopen command.

"reopenOption": {
"enabled": true, // Master switch for the reopen feature (button + /reopen)
"button": true, // Show the โ™ป๏ธ Reopen button on the closed-ticket message
"whoCanReopen": "STAFFONLY" // "EVERYONE" or "STAFFONLY"
}
FieldDescription
enabledMaster switch. When false, the button is hidden and /reopen replies that it is disabled.
buttonWhether the โ™ป๏ธ Reopen button is shown on the closed-ticket message.
whoCanReopen"STAFFONLY" (default) requires staff. "EVERYONE" allows anyone who can see the channel.

Reopening restores the creator's channel access, moves the channel back to its ticket type's category and drops the closed- name prefix.

Note: Closed channels are usually only visible to staff (the creator's view is removed on close), so "EVERYONE" mainly matters if you keep closed channels visible to users.

Transcript Design & Languageโ€‹

The generated HTML transcript can be rendered in two styles and any of the built-in languages:

"transcriptDesign": "modern", // "modern" (default) or "classic"
"transcriptLang": "en" // en, de, fr, es, pt, pl, hu. Falls back to English if omitted/unsupported
ValueLook
"modern"Minimal, MSK-branded layout (default, also used if the key is absent).
"classic"The original Discord-style dark layout.

transcriptLang localizes all transcript labels (header fields, section title, footer, copy-button tooltip) and the date format. Seven languages are built in (en, de, fr, es, pt, pl, hu); any other value falls back to English. The bot's own messages use the separate lang key, which accepts the same set.

Both styles are fully self-contained / offline-safe (no external requests):

  • Avatars and custom emojis are embedded as Base64 (custom emojis fall back to :name: text if the image can't be fetched at generation time).
  • User mentions and the header fields Created by / Claimed by / Closed by are shown as display names instead of raw user IDs (unresolvable IDs fall back to the ID).
  • The header also shows Closed by and the close reason, the reason only when one was provided.
  • Fenced code blocks show their language as a small label (e.g. LUA) and have a copy button that copies the block to the clipboard. No syntax colouring (kept dependency-free).

Statisticsโ€‹

/stats shows server-wide numbers. /stats @user shows a detailed profile split into ๐Ÿ‘ค As a User and ๐Ÿ›ก๏ธ As Staff.