diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 59c6ac0..c7cc36e 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,7 +1,7 @@
# Contributing Guidelines
-[](https://discord.com/invite/26vT9wt3n3) [](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.3) [](https://github.com/sponsors/DJj123dj)
+[](https://discord.com/invite/26vT9wt3n3) [](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0) [](https://github.com/sponsors/DJj123dj)
These are the Contributing Guidelines of Open Ticket!
Here you can find everything you need to know about contributing to Open Ticket.
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index 3f704b6..a381a61 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -19,18 +19,15 @@ This list will be updated on every release.
| Version | Supported | Notes |
|------------|-----------|---------------------------------------------------------------|
-| 4.2.0 | ๐ฆ | In Development |
-| 4.1.x | ๐ฆ | In Development |
-| 4.1.3 | โ
| |
-| 4.1.2 | โ
| |
-| 4.1.1 | โ
| Supported Until April 2026 (LTS) |
+| 4.2.x | ๐ฆ | In Development |
+| 4.2.1 | ๐ฆ | In Development |
+| 4.2.0 | โ
| |
+| 4.1.3 | โ
| (LTS) Long-Term-Support, Until September 2026 |
+| 4.1.2 | ๐ง | |
+| 4.1.1 | ๐ง | |
| 4.1.0 | ๐ง | |
-| 4.0.7 | ๐ง | |
-| 4.0.6 | ๐ง | |
-| 4.0.5 | ๐ง | Deprecated |
-| 4.0.4 | ๐ง | Deprecated |
-| < 4.0.4 | ๐ง | Deprecated, Transcripts v2.0, Documentation Only |
-| < 4.0.0 | โ | |
+| 4.0.7 | ๐ง | Deprecated |
+| < 4.0.7 | โ | |
### ๐ท๏ธ Reporting Vulnerabilities
You can report vulnerabilities, errors & bugs using one of the following methods:
diff --git a/.tools/pterodactyl-eggs/README.md b/.tools/pterodactyl-eggs/README.md
index 4585e89..c22f4a4 100644
--- a/.tools/pterodactyl-eggs/README.md
+++ b/.tools/pterodactyl-eggs/README.md
@@ -2,7 +2,7 @@
[](https://discord.com/invite/26vT9wt3n3)
-[](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.3)
+[](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0)
[](https://github.com/sponsors/DJj123dj)
[](.eggs/README.md)
@@ -20,6 +20,9 @@ It's recommended to provide at least `1GB` of **Memory/RAM** and `5GB` of **disk
[**`openticket-egg-main.json` (Recommended)**](openticket-egg-main.json)
- This egg will use the `main` branch of Open Ticket.
+[**`openticket-egg-v4.2.0.json`**](openticket-egg-v4.2.0.json)
+- This egg will always use Open Ticket `v4.2.0`. Open Ticket updates will not have an effect on this egg.
+
[**`openticket-egg-v4.1.3.json`**](openticket-egg-v4.1.3.json)
- This egg will always use Open Ticket `v4.1.3`. Open Ticket updates will not have an effect on this egg.
diff --git a/.tools/pterodactyl-eggs/openticket-egg-v4.2.0.json b/.tools/pterodactyl-eggs/openticket-egg-v4.2.0.json
new file mode 100644
index 0000000..88530ca
--- /dev/null
+++ b/.tools/pterodactyl-eggs/openticket-egg-v4.2.0.json
@@ -0,0 +1,62 @@
+{
+ "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
+ "meta": {
+ "version": "PTDL_v2",
+ "update_url": null
+ },
+ "exported_at": "2025-03-16T18:10:18+01:00",
+ "name": "Open Ticket (v4.2.0)",
+ "author": "support@dj-dj.be",
+ "description": "This is the official Pterodactyl egg for Open Ticket v4.2.0, the most advanced & customisable discord ticket bot that you will ever find! You can customise up to 300+ variables! This includes Html Transcripts, Advanced Plugins, Custom Embeds, Questions\/Modals, Stats & more!",
+ "features": null,
+ "docker_images": {
+ "ghcr.io\/parkervcp\/yolks:nodejs_20": "ghcr.io\/parkervcp\/yolks:nodejs_20"
+ },
+ "file_denylist": [],
+ "startup": "if [[ ! -z ${NODE_PACKAGES} ]]; then npm install ${NODE_PACKAGES}; fi; if [[ ! -z ${UNNODE_PACKAGES} ]]; then npm uninstall ${UNNODE_PACKAGES}; fi; if [ -f \/home\/container\/package.json ]; then npm install; fi; node \"\/home\/container\/index.js\" ${NODE_FLAGS};",
+ "config": {
+ "files": "{}",
+ "startup": "{\r\n \"done\": \"STARTUP INFO:\"\r\n}",
+ "logs": "{}",
+ "stop": "^C"
+ },
+ "scripts": {
+ "installation": {
+ "script": "#!\/bin\/bash\r\n# Open Ticket Installation Script (v4.2.0)\r\n# Inspired by: Node.js Installation Script\r\n# \u00a9 DJdj Development\r\n\r\necho -e \"[OT INSTALLER] installing dependencies. please wait...\"\r\napt update\r\napt install -y git curl\r\n\r\necho -e \"[OT INSTALLER] updating npm. please wait...\"\r\nnpm install npm@latest --location=global\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nBRANCH=\"v4.2.0\"\r\n\r\nif [ \"$(ls -A \/mnt\/server)\" ]; then\r\n echo -e \"[OT INSTALLER] \/mnt\/server directory is not empty.\"\r\n if [ -d .git ]; then\r\n echo -e \"[OT INSTALLER] .git directory exists\"\r\n if [ -f .git\/config ]; then\r\n echo -e \"[OT INSTALLER] loading info from git config\"\r\n ORIGIN=$(git config --get remote.origin.url)\r\n else\r\n echo -e \"[OT INSTALLER] files found with no git config\"\r\n echo -e \"[OT INSTALLER] closing out without touching things to not break anything\"\r\n exit 10\r\n fi\r\n fi\r\n\r\n if [ \"${ORIGIN}\" == \"https:\/\/github.com\/open-discord-bots\/open-ticket.git\" ]; then\r\n echo \"pulling latest from github\"\r\n git pull\r\n fi\r\nelse\r\n echo -e \"[OT INSTALLER] \/mnt\/server is empty.\"\r\n echo -e \"[OT INSTALLER] cloning files into repo.\"\r\n if [ -z ${BRANCH} ]; then\r\n echo -e \"[OT INSTALLER] cloning default branch\"\r\n git clone https:\/\/github.com\/open-discord-bots\/open-ticket.git .\r\n else\r\n echo -e \"[OT INSTALLER] cloning ${BRANCH}'\"\r\n git clone --single-branch --branch ${BRANCH} https:\/\/github.com\/open-discord-bots\/open-ticket.git .\r\n fi\r\nfi\r\n\r\necho -e \"[OT INSTALLER] installing Open Ticket node.js packages.\"\r\nif [ -f \/mnt\/server\/package.json ]; then\r\n \/usr\/local\/bin\/npm install --omit=dev\r\nfi\r\n\r\necho -e \"[OT INSTALLER] installing custom node.js packages.\"\r\nif [[ ! -z ${NODE_PACKAGES} ]]; then\r\n \/usr\/local\/bin\/npm install ${NODE_PACKAGES}\r\nfi\r\n\r\necho -e \"[OT INSTALLER] install complete!\"\r\nexit 0",
+ "container": "node:latest",
+ "entrypoint": "bash"
+ }
+ },
+ "variables": [
+ {
+ "name": "Additional Npm Packages",
+ "description": "Specify additional npm packages used by Open Ticket plugins. Use spaces to separate.",
+ "env_variable": "NODE_PACKAGES",
+ "default_value": "",
+ "user_viewable": false,
+ "user_editable": true,
+ "rules": "string|nullable",
+ "field_type": "text"
+ },
+ {
+ "name": "Startup Flags",
+ "description": "Start the bot with additional Open Ticket flags. Separate using spaces.\r\nA full reference list can be found in the documentation.",
+ "env_variable": "NODE_FLAGS",
+ "default_value": "",
+ "user_viewable": false,
+ "user_editable": true,
+ "rules": "string|nullable",
+ "field_type": "text"
+ },
+ {
+ "name": "Uninstall Npm Packages",
+ "description": "A list of npm packages to uninstall. Separate by spaces.",
+ "env_variable": "UNNODE_PACKAGES",
+ "default_value": "",
+ "user_viewable": false,
+ "user_editable": true,
+ "rules": "string|nullable",
+ "field_type": "text"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index b3a38e2..16c90c3 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
Related Projects:

-
+
diff --git a/config/general.json b/config/general.json
deleted file mode 100644
index 9c39bad..0000000
--- a/config/general.json
+++ /dev/null
@@ -1,125 +0,0 @@
-{
- "_INFO":{
- "support":"https://otdocs.dj-dj.be",
- "discord":"https://discord.dj-dj.be",
- "version":"open-ticket-v4.1.3"
- },
-
- "token":"INSERT_BOT_TOKEN (leave empty if using 'tokenFromENV')",
- "tokenFromENV":false,
-
- "mainColor":"#f8ba00",
- "language":"english",
- "prefix":"!ticket ",
- "serverId":"DISCORD_SERVER_ID",
- "globalAdmins":["DISCORD_ROLE_ID"],
-
- "slashCommands":true,
- "textCommands":true,
-
- "status":{
- "enabled":true,
- "type":"listening OR watching OR playing OR custom",
- "mode":"online OR invisible OR idle OR dnd",
- "text":"/help",
- "state":"Insert additional status text (or leave empty)"
- },
-
- "system":{
- "preferSlashOverText":true,
- "sendErrorOnUnknownCommand":true,
- "questionFieldsInCodeBlock":true,
- "displayFieldsWithQuestions":false,
- "showGlobalAdminsInPanelRoles":false,
- "disableVerifyBars":false,
- "useRedErrorEmbeds":true,
- "alwaysShowReason":false,
- "emojiStyle":"before (OR after OR double OR disabled)",
- "pinEmoji":"๐",
-
- "replyOnTicketCreation":true,
- "replyOnReactionRole":true,
- "askPriorityOnTicketCreation":false,
- "removeParticipantsOnClose":false,
- "disableAutocloseAfterReopen":true,
- "autodeleteRequiresClosedTicket":true,
- "adminOnlyDeleteWithoutTranscript":true,
- "allowCloseBeforeMessage":false,
- "allowCloseBeforeAdminMessage":true,
- "useTranslatedConfigChecker":true,
- "pinFirstTicketMessage":false,
-
- "enableTicketClaimButtons":true,
- "enableTicketCloseButtons":true,
- "enableTicketPinButtons":true,
- "enableTicketDeleteButtons":true,
- "enableTicketActionWithReason":true,
- "enableDeleteWithoutTranscript":true,
-
- "logs":{
- "enabled":false,
- "channel":"DISCORD_CHANNEL_ID"
- },
-
- "limits":{
- "enabled":true,
- "globalMaximum":50,
- "userMaximum":3
- },
-
- "channelTopic":{
- "showOptionName":true,
- "showOptionDescription":false,
- "showOptionTopic":true,
- "showPriority":false,
- "showClosed":true,
- "showClaimed":false,
- "showPinned":false,
- "showCreator":false,
- "showParticipants":false
- },
-
- "permissions":{
- "help":"everyone (OR admin OR none OR role id)",
- "panel":"admin (OR everyone OR none OR role id)",
- "ticket":"none (OR admin OR everyone OR role id)",
- "close":"everyone (OR admin OR none OR role id)",
- "delete":"admin (OR everyone OR none OR role id)",
- "reopen":"everyone (OR admin OR none OR role id)",
- "claim":"admin (OR everyone OR none OR role id)",
- "unclaim":"admin (OR everyone OR none OR role id)",
- "pin":"admin (OR everyone OR none OR role id)",
- "unpin":"admin (OR everyone OR none OR role id)",
- "move":"admin (OR everyone OR none OR role id)",
- "rename":"admin (OR everyone OR none OR role id)",
- "add":"admin (OR everyone OR none OR role id)",
- "remove":"admin (OR everyone OR none OR role id)",
- "blacklist":"admin (OR everyone OR none OR role id)",
- "stats":"everyone (OR admin OR none OR role id)",
- "clear":"admin (OR everyone OR none OR role id)",
- "autoclose":"admin (OR everyone OR none OR role id)",
- "autodelete":"admin (OR everyone OR none OR role id)",
- "transfer":"admin (OR everyone OR none OR role id)",
- "topic":"admin (OR everyone OR none OR role id)",
- "priority":"admin (OR everyone OR none OR role id)"
- },
-
- "messages":{
- "creation":{"dm":false,"logs":true},
- "closing":{"dm":false,"logs":true},
- "deleting":{"dm":false,"logs":true},
- "reopening":{"dm":false,"logs":true},
- "claiming":{"dm":false,"logs":true},
- "pinning":{"dm":false,"logs":true},
- "adding":{"dm":false,"logs":true},
- "removing":{"dm":false,"logs":true},
- "renaming":{"dm":false,"logs":true},
- "moving":{"dm":false,"logs":true},
- "blacklisting":{"dm":false,"logs":true},
- "transferring":{"dm":false,"logs":true},
- "topicChange":{"dm":false,"logs":true},
- "priorityChange":{"dm":false,"logs":true},
- "reactionRole":{"dm":false,"logs":true}
- }
- }
-}
\ No newline at end of file
diff --git a/config/general.jsonc b/config/general.jsonc
new file mode 100644
index 0000000..ec0c4ba
--- /dev/null
+++ b/config/general.jsonc
@@ -0,0 +1,175 @@
+/*
+ * Hi there! Thank you for installing Open Ticket.
+ * ----------------------------------------------
+ * If you need any assistance with configuring the bot,
+ * feel free to use the documentation or join our Discord server:
+ * https://otdocs.dj-dj.be
+ * https://discord.dj-dj.be
+ * ----------------------------------------------
+ * SETUP:
+ * 1. Install the required dependencies using the command: "npm install"
+ * 2. Configure the bot in one of the following ways:
+ * a. (easy) Using the Quick Setup CLI Tool
+ * b. (difficult) Using the JSON files in `./config/`
+ *
+ * Start the Quick Setup CLI Tool using the command: "npm run setup"
+ * After configuration, start the bot using the command: "npm start"
+ *
+ * Good luck! DJj123dj & contributors
+ */
+{
+ "_CONFIG_VERSION":"open-ticket-v4.2.0",
+
+ /* Load the bot token from .env or the "token" field below. Leave "token" empty if using "tokenFromENV". */
+ "token":"INSERT_BOT_TOKEN",
+ "tokenFromENV":false,
+
+ "mainColor":"#f8ba00", //Hex color used in most embeds
+ "language":"english", //Visit README.md for list
+ "prefix":"!ticket ", //Prefix used in text commands
+ "serverId":"DISCORD_SERVER_ID",
+ "globalAdmins":["DISCORD_ROLE_ID"], //Have access to all commands
+
+ /* Enable/disable text or slash commands. */
+ "slashCommands":true,
+ "textCommands":true,
+
+ /* Configure the status of the bot. */
+ "status":{
+ "enabled":true,
+ "type":"listening", //Choices: listening, watching, playing, custom
+ "mode":"online", //Choices: online, invisible, idle, dnd
+ "text":"/help",
+ "state":"" //Additional text (Leave empty to disable)
+ },
+
+ /* Send ticket logs to a channel or in DM of the ticket creator. */
+ "logs":{
+ "enabled":false,
+ "channel":"DISCORD_CHANNEL_ID",
+ "logMessages":{
+ "creation":{"dm":false,"logs":true},
+ "closing":{"dm":false,"logs":true},
+ "deleting":{"dm":false,"logs":true},
+ "reopening":{"dm":false,"logs":true},
+ "claiming":{"dm":false,"logs":true},
+ "pinning":{"dm":false,"logs":true},
+ "adding":{"dm":false,"logs":true},
+ "removing":{"dm":false,"logs":true},
+ "renaming":{"dm":false,"logs":true},
+ "moving":{"dm":false,"logs":true},
+ "blacklisting":{"dm":false,"logs":true},
+ "transferring":{"dm":false,"logs":true},
+ "topicChange":{"dm":false,"logs":true},
+ "priorityChange":{"dm":false,"logs":true},
+ "reactionRole":{"dm":false,"logs":true}
+ }
+ },
+
+ /* A large collection of settings for the ticket system. */
+ "ticketSystem":{
+ "preferSlashOverText":true, //Show slashcmds in help menu's
+ "sendErrorOnUnknownCommand":true, //Send error when command not found
+ "questionFieldsInCodeBlock":true, //Put question answers in code blocks
+ "displayFieldsWithQuestions":false, //Display embed fields together with question answers
+ "showGlobalAdminsInPanelRoles":false, //Include "globalAdmins" in panel admin lists
+ "disableVerifyBars":false, //Disable the (โ/โ
) buttons
+ "useRedErrorEmbeds":true, //Make errors embeds always red
+ "alwaysShowReason":false, //Show reason even if none is provided
+ "emojiStyle":"before", //The style of emoji's in embeds. Choices: before, after, double, disabled
+ "pinEmoji":"๐", //Channel emoji of pinned tickets (Leave empty to disable)
+ "closeEmoji":"๐", //Channel emoji of closed tickets (Leave empty to disable)
+
+ "replyOnTicketCreation":true, //Reply with a msg when a ticket is created
+ "replyOnReactionRole":true, //Reply with a msg when a reaction role is used
+ "askPriorityOnTicketCreation":true, //Show a dropdown to select priority
+ "removeParticipantsOnClose":false, //Remove non-admins when ticket is closed
+ "disableAutocloseAfterReopen":true, //Disable autoclose after ticket got reopened
+ "autodeleteRequiresClosedTicket":true, //A ticket must be closed before autodelete works
+ "adminOnlyDeleteWithoutTranscript":true, //Only allow "globalAdmins" to delete a ticket without transcript
+ "allowCloseBeforeMessage":false, //Allow closing before a message is sent
+ "allowCloseBeforeAdminMessage":true, //Allow closing before an admin has sent a message
+ "useTranslatedConfigChecker":true, //Translate config errors in the console
+ "pinFirstTicketMessage":true, //Pin the ticket message to the channel
+
+ /* Enable/disable certain buttons & features of the bot. */
+ "enableTicketClaimButtons":true,
+ "enableTicketCloseButtons":true,
+ "enableTicketPinButtons":true,
+ "enableTicketDeleteButtons":true,
+ "enableTicketActionWithReason":true,
+ "enableDeleteWithoutTranscript":true, //Allow deleting tickets without transcript
+ "enableCreateTicketForOtherUser":true, //Allow creating tickets for other users
+
+ /* Set the maximum amount of simultaneous tickets. */
+ "limits":{
+ "enabled":true,
+ "globalMaximum":50,
+ "userMaximum":3
+ },
+
+ /* Choose which data is shown in the channel topic. */
+ "channelTopic":{
+ "showOptionName":true,
+ "showOptionDescription":false,
+ "showOptionTopic":true,
+ "showPriority":false,
+ "showClosed":true,
+ "showClaimed":false,
+ "showPinned":false,
+ "showCreator":false,
+ "showParticipants":false
+ },
+
+ /* Move closed tickets to a separate category. */
+ "closedCategory":{
+ "enabled":false,
+ "categoryId":"DISCORD_CATEGORY_ID"
+ },
+
+ /* Create tickets in a backup category when the original category exceeds 50 channels. */
+ "backupCategory":{
+ "enabled":false,
+ "categoryId":"DISCORD_CATEGORY_ID"
+ },
+
+ /* Move claimed tickets to a matching category of the user that claimed the ticket. Set to empty list [] to disable. */
+ "claimedCategories":[
+ {"user":"DISCORD_USER_ID","category":"DISCORD_CATEGORY_ID"}
+ ]
+ },
+
+ /*
+ * Set permissions for each individual command, button or action.
+ * CHOICES:
+ * >> "none" -> Command disabled
+ * >> "everyone" -> Allowed for everyone
+ * >> "admin" -> Global & ticket admins only
+ * >> "DISCORD_ROLE_ID" -> Custom role only
+ */
+ "permissions":{
+ "help":"everyone",
+ "panel":"admin",
+ "ticket":"none",
+ "close":"everyone",
+ "delete":"admin",
+ "reopen":"everyone",
+ "claim":"admin",
+ "unclaim":"admin",
+ "pin":"admin",
+ "unpin":"admin",
+ "move":"admin",
+ "rename":"admin",
+ "add":"admin",
+ "remove":"admin",
+ "blacklist":"admin",
+ "stats":"everyone",
+ "clear":"admin",
+ "autoclose":"admin",
+ "autodelete":"admin",
+ "transfer":"admin",
+ "topic":"admin",
+ "priority":"admin",
+ "transcripts":"admin"
+ }
+}
\ No newline at end of file
diff --git a/config/options.json b/config/options.json
deleted file mode 100644
index 331864a..0000000
--- a/config/options.json
+++ /dev/null
@@ -1,126 +0,0 @@
-[
- {
- "id":"example-ticket",
- "name":"Question",
- "description":"Want to tell us something? Create this ticket for general questions for our support team. (leave empty to disable)",
- "type":"ticket",
-
- "button":{
- "emoji":"๐ซ (leave empty to disable)",
- "label":"question (leave empty to disable)",
- "color":"gray OR red OR green OR blue"
- },
-
- "ticketAdmins":["DISCORD_ROLE_ID"],
- "readonlyAdmins":["DISCORD_ROLE_ID"],
- "allowCreationByBlacklistedUsers":false,
- "questions":["example-question-1","example-question-2"],
-
- "channel":{
- "prefix":"question-",
- "suffix":"user-name OR user-id OR random-number OR random-hex OR counter-dynamic OR counter-fixed",
- "category":"category id (leave empty to disable)",
- "closedCategory":"category id (leave empty to disable)",
- "backupCategory":"category id (leave empty to disable)",
- "claimedCategory":[
- {"user":"USER_ID","category":"CATEGORY_ID"}
- ],
- "topic":"This is the ticket topic shown in the channel. (leave empty to disable)"
- },
-
- "dmMessage":{
- "enabled":false,
- "text":"",
- "embed":{
- "enabled":false,
- "title":"Question Ticket (leave empty to disable)",
- "description":"Thank you for creating a ticket in our server. We will try to help you as soon as possible. (leave empty to disable)",
- "customColor":"#f8ab00 (leave empty to disable)",
-
- "image":"https://www.example.com/image.png (leave empty to disable)",
- "thumbnail":"https://www.example.com/image.png (leave empty to disable)",
- "fields":[
- {"name":"Field name","value":"Field value","inline":false}
- ],
- "timestamp":false
- }
- },
- "ticketMessage":{
- "enabled":true,
- "text":"",
- "embed":{
- "enabled":true,
- "title":"Question Ticket",
- "description":"Thank you for creating a ticket in our server.\nOur support team will assist you as soon as possible. (leave empty to disable)",
- "customColor":"#f8ab00 (leave empty to disable)",
-
- "image":"https://www.example.com/image.png (leave empty to disable)",
- "thumbnail":"https://www.example.com/image.png (leave empty to disable)",
- "fields":[
- {"name":"Field name","value":"Field value","inline":false}
- ],
- "timestamp":false
- },
- "ping":{
- "@here":true,
- "@everyone":false,
- "custom":["DISCORD_ROLE_ID"]
- }
- },
- "autoclose":{
- "enableInactiveHours":false,
- "inactiveHours":24,
- "enableUserLeave":false,
- "disableOnClaim":false
- },
- "autodelete":{
- "enableInactiveDays":false,
- "inactiveDays":7,
- "enableUserLeave":false,
- "disableOnClaim":false
- },
- "cooldown":{
- "enabled":false,
- "cooldownMinutes":10
- },
- "limits":{
- "enabled":false,
- "globalMaximum":20,
- "userMaximum":3
- },
- "slowMode":{
- "enabled":false,
- "slowModeSeconds":20
- }
- },
- {
- "id":"example-website",
- "name":"Website",
- "description":"Take a look at our amazing website by clicking the button.",
- "type":"website",
-
- "button":{
- "emoji":"๐",
- "label":"Visit Website"
- },
-
- "url":"https://www.dj-dj.be"
- },
- {
- "id":"example-role",
- "name":"Update Ping",
- "description":"Click here to receive notifications about updates.",
- "type":"role",
-
- "button":{
- "emoji":"๐ข",
- "label":"Update Ping",
- "color":"gray OR red OR green OR blue"
- },
-
- "roles":["DISCORD_ROLE_ID"],
- "mode":"add&remove OR remove OR add",
- "removeRolesOnAdd":["DISCORD_ROLE_ID"],
- "addOnMemberJoin":false
- }
-]
\ No newline at end of file
diff --git a/config/options.jsonc b/config/options.jsonc
new file mode 100644
index 0000000..1948f6e
--- /dev/null
+++ b/config/options.jsonc
@@ -0,0 +1,165 @@
+/*
+ * OPEN TICKET BUTTON OPTIONS
+ * ----------------------------------------------
+ * Create customizable ticket, website, reaction-role or sub-panel button options.
+ * Up to 25 options can be added to each panel in (config/panels.jsonc)
+ * There are 4 types of options available: ticket, website, role, sub-panel
+ *
+ * TIP: Create new options by copying everything between and including the {...} brackets of an option. Paste it after the last option and make sure that they are seperated by a comma.
+ */
+[
+ {
+ /* A ticket option creates a button to open a ticket. */
+ "id":"example-ticket",
+ "name":"Question",
+ "description":"Want to tell us something? Create this ticket for general questions for our support team.", //Leave empty to disable
+ "type":"ticket",
+
+ "button":{
+ /* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
+ "emoji":"๐ซ",
+ "label":"Question",
+ "color":"gray" //Choices: gray, red, green, blue
+ },
+
+ /* Add up to 5 modal questions IDs from (config/questions.jsonc). */
+ "questions":["example-question-1","example-question-2"],
+
+ "ticketAdmins":["DISCORD_ROLE_ID"],
+ "readonlyAdmins":["DISCORD_ROLE_ID"],
+ "allowCreationByBlacklistedUsers":false,
+
+ "channel":{
+ /* Configure the name, topic and category of the ticket option. */
+ "prefix":"question-",
+ "suffix":"user-name", //Choices: user-name, user-id, random-number, random-hex, counter-dynamic, counter-fixed
+ "category":"DISCORD_CATEGORY_ID", //Leave empty to disable
+ "topic":"The ticket topic shown in the channel." //Leave empty to disable
+ },
+
+ "dmMessage":{
+ /* Send a customisable message in DM when creating a ticket. */
+ "enabled":false,
+ "text":"", //Leave empty to disable
+ "embed":{
+ "enabled":false,
+ "title":"Question Ticket", //Leave empty to disable
+ "description":"Thank you for creating a ticket in our server. We will try to help you as soon as possible.", //Leave empty to disable
+ "customColor":"#f8ab00", //Leave empty to use default color
+
+ "image":"", //Image URL. Leave empty to disable
+ "thumbnail":"", //Image URL. Leave empty to disable
+ /* Embed fields. Set to empty list [] to disable. */
+ "fields":[
+ {"name":"Field name","value":"Field value","inline":false}
+ ],
+ "timestamp":false
+ }
+ },
+ "ticketMessage":{
+ /* Send a customisable message in the ticket with close, claim, delete, ... buttons. */
+ "enabled":true,
+ "text":"", //Leave empty to disable
+ "embed":{
+ "enabled":true,
+ "title":"Question Ticket", //Leave empty to disable
+ "description":"Thank you for creating a ticket in our server.\nOur support team will assist you as soon as possible. (leave empty to disable)", //Leave empty to disable
+ "customColor":"#f8ab00 (leave empty to disable)", //Leave empty to use default color
+
+ "image":"https://www.example.com/image.png (leave empty to disable)", //Image URL. Leave empty to disable
+ "thumbnail":"https://www.example.com/image.png (leave empty to disable)", //Image URL. Leave empty to disable
+ /* Embed fields. Set to empty list [] to disable. */
+ "fields":[
+ {"name":"Field name","value":"Field value","inline":false}
+ ],
+ "timestamp":false
+ },
+ "ping":{
+ /* Customise the user & role mentions of this ticket message. */
+ "@here":true,
+ "@everyone":false,
+ "custom":["DISCORD_ROLE_ID"]
+ }
+ },
+ "autoclose":{
+ /* Autoclose this ticket after a period of inactivity or when the creator leaves the server. */
+ "enableInactiveHours":false,
+ "inactiveHours":24,
+ "enableUserLeave":false,
+ "disableOnClaim":false
+ },
+ "autodelete":{
+ /* Autodelete this ticket after a period of inactivity or when the creator leaves the server. */
+ "enableInactiveDays":false,
+ "inactiveDays":7,
+ "enableUserLeave":false,
+ "disableOnClaim":false
+ },
+ "cooldown":{
+ /* Users must wait a certain period before being able to create another ticket of this type. */
+ "enabled":false,
+ "cooldownMinutes":10
+ },
+ "limits":{
+ /* Set the maximum amount of simultaneous tickets of this option. */
+ "enabled":false,
+ "globalMaximum":20,
+ "userMaximum":3
+ },
+ "slowMode":{
+ /* Enable slow-mode in the ticket channel. */
+ "enabled":false,
+ "slowModeSeconds":20
+ }
+ },
+ {
+ /* A website option creates a button with a URL to an external website. */
+ "id":"example-website",
+ "name":"Website",
+ "description":"Take a look at our amazing website.", //Leave empty to disable
+ "type":"website",
+
+ "button":{
+ /* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
+ "emoji":"๐",
+ "label":"Visit Website"
+ },
+
+ "url":"https://www.dj-dj.be"
+ },
+ {
+ /* A reaction-role option creates a button for members to choose roles. */
+ "id":"example-role",
+ "name":"Update Ping",
+ "description":"Receive notifications about updates in our server.", //Leave empty to disable
+ "type":"role",
+
+ "button":{
+ /* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
+ "emoji":"๐ข",
+ "label":"Update Ping",
+ "color":"gray" //Choices: gray, red, green, blue
+ },
+
+ "roles":["DISCORD_ROLE_ID"],
+ "mode":"add&remove OR remove OR add", //What to do with the roles. Choices: add&remove, add, remove
+ "removeRolesOnAdd":["DISCORD_ROLE_ID"], //Remove these old roles when new roles are added.
+ "addOnMemberJoin":false //Add these roles automatically when joining the server.
+ },
+ {
+ /* A sub-panel option creates a button which sends another panel for additional options. */
+ "id":"example-sub-panel",
+ "name":"Example Sub-Panel",
+ "description":"This is an example of how to implement a sub-panel in Open Ticket.", //Leave empty to disable
+ "type":"sub-panel",
+
+ "button":{
+ /* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
+ "emoji":"๐",
+ "label":"Sub-Panel Example",
+ "color":"gray" //Choices: gray, red, green, blue
+ },
+
+ "subPanelId":"example-panel" //Choose a panel ID from (config/panels.jsonc)
+ }
+]
\ No newline at end of file
diff --git a/config/panels.json b/config/panels.json
deleted file mode 100644
index 20faddd..0000000
--- a/config/panels.json
+++ /dev/null
@@ -1,39 +0,0 @@
-[
- {
- "id":"example-embed",
- "name":"Example Embed",
- "dropdown":false,
- "options":["example-ticket","example-website","example-role"],
-
- "text":"",
- "embed":{
- "enabled":true,
- "title":"Tickets:",
- "description":"Create a ticket by selecting one of the options below. Once selected, a private ticket will be created for you and our support team will be able to assist you directly.",
-
- "customColor":"#f8ab00 (leave empty to disable)",
- "url":"https://openticket.dj-dj.be (leave empty to disable)",
-
- "image":"https://www.example.com/image.png (leave empty to disable)",
- "thumbnail":"https://www.example.com/image.png (leave empty to disable)",
-
- "footer":"Open Ticket v4.1.3 (leave empty to disable)",
- "fields":[
- {"name":"Field name","value":"Field value","inline":false}
- ],
- "timestamp":false
- },
- "settings":{
- "dropdownPlaceholder":"Choose a ticket type",
-
- "enableMaxTicketsWarningInText":false,
- "enableMaxTicketsWarningInEmbed":true,
-
- "describeOptionsLayout":"simple OR normal OR detailed",
- "describeOptionsCustomTitle":"",
- "describeOptionsInText":false,
- "describeOptionsInEmbedFields":true,
- "describeOptionsInEmbedDescription":false
- }
- }
-]
\ No newline at end of file
diff --git a/config/panels.jsonc b/config/panels.jsonc
new file mode 100644
index 0000000..cf5bca8
--- /dev/null
+++ b/config/panels.jsonc
@@ -0,0 +1,57 @@
+/*
+ * OPEN TICKET PANELS
+ * ----------------------------------------------
+ * Create customizable panel messages with buttons or a dropdown.
+ * Add up to 25 options to this panel from (config/options.jsonc)
+ * Panels can be customised with text, images, colors and more.
+ *
+ * Spawn the panel in Discord using: /panel
+ *
+ * TIP: Create new panels by copying everything between and including the {...} brackets of an panel. Paste it after the last panel and make sure that they are seperated by a comma.
+ */
+[
+ {
+ /* A panel is creates a message with up to 25 options as buttons or dropdown. */
+ "id":"example-panel",
+ "name":"Example Panel",
+ "dropdown":false,
+
+ /* Add up to 5 option IDs from (config/options.jsonc). */
+ "options":["example-ticket","example-website","example-role"],
+
+ "text":"", //Leave empty to disable
+ "embed":{
+ "enabled":true,
+ "title":"Tickets:", //Leave empty to disable
+ "description":"Create a ticket by selecting one of the options below. Once selected, a private ticket will be created for you and our support team will be able to assist you directly.", //Leave empty to disable
+
+ "customColor":"#f8ab00", //Leave empty to use default color
+ "url":"", //URL. Leave empty to disable
+
+ "image":"", //Image URL. Leave empty to disable
+ "thumbnail":"", //Image URL. Leave empty to disable
+
+ "footer":"Open Ticket v4.2.0", //Leave empty to disable
+ /* Embed fields. Set to empty list [] to disable. */
+ "fields":[
+ {"name":"Field name","value":"Field value","inline":false}
+ ],
+ "timestamp":false
+ },
+ "settings":{
+ "dropdownPlaceholder":"Create a ticket...", //Leave empty to use default.
+ "maximumButtonsPerRow":5,
+
+ /* Display the maximum amount of tickets per user. */
+ "enableMaxTicketsWarningInText":false,
+ "enableMaxTicketsWarningInEmbed":true,
+
+ /* Automatically generate option descriptions from (config/options.jsonc). */
+ "describeOptionsLayout":"normal", //Choices: simple, normal, detailed
+ "describeOptionsCustomTitle":"",
+ "describeOptionsInText":false,
+ "describeOptionsInEmbedFields":true,
+ "describeOptionsInEmbedDescription":false
+ }
+ }
+]
\ No newline at end of file
diff --git a/config/questions.json b/config/questions.json
deleted file mode 100644
index 3d36da9..0000000
--- a/config/questions.json
+++ /dev/null
@@ -1,28 +0,0 @@
-[
- {
- "id":"example-question-1",
- "name":"Example Question 1",
- "type":"short",
-
- "required":true,
- "placeholder":"An example short text question.",
- "length":{
- "enabled":false,
- "min":0,
- "max":1000
- }
- },
- {
- "id":"example-question-2",
- "name":"Example Question 2",
- "type":"paragraph",
-
- "required":false,
- "placeholder":"An example paragraph text question.",
- "length":{
- "enabled":false,
- "min":0,
- "max":1000
- }
- }
-]
\ No newline at end of file
diff --git a/config/questions.jsonc b/config/questions.jsonc
new file mode 100644
index 0000000..8f9062e
--- /dev/null
+++ b/config/questions.jsonc
@@ -0,0 +1,102 @@
+/*
+ * OPEN TICKET MODAL QUESTIONS
+ * ----------------------------------------------
+ * Create customizable modal questions that will be shown before creating a ticket.
+ * Each ticket option (config/options.jsonc) can contain a maximum of 5 questions.
+ * There are 6 types of questions available: short, paragraph, dropdown, radio-select, checkbox-select, text-display
+ *
+ * TIP: Create new questions by copying everything between and including the {...} brackets of a question. Paste it after the last question and make sure that they are seperated by a comma.
+ */
+[
+ {
+ /* A short text input modal question. */
+ "id":"example-question-1",
+ "name":"Example Question 1",
+ "description":"", //Leave empty to disable
+ "type":"short",
+ "required":true,
+
+ "placeholder":"Insert answer...",
+ "length":{
+ /* Configure length limits for the answer. */
+ "enabled":false,
+ "min":0,
+ "max":1000
+ }
+ },
+ {
+ /* A paragraph text input modal question. */
+ "id":"example-question-2",
+ "name":"Example Question 2",
+ "description":"", //Leave empty to disable
+ "type":"paragraph",
+ "required":false,
+
+ "placeholder":"Insert answer...",
+ "length":{
+ /* Configure length limits for the answer. */
+ "enabled":false,
+ "min":0,
+ "max":1000
+ }
+ },
+ {
+ /* A dropdown menu input modal question with up to 25 choices. "emoji" & "description" fields are optional. */
+ "id":"example-question-3",
+ "name":"Example Question 3",
+ "description":"This is a dropdown question.", //Leave empty to disable
+ "type":"dropdown",
+ "required":false,
+
+ "placeholder":"Choose your answer...",
+ "choices":[
+ {"title":"Choice A","description":"Apple","emoji":"๐"},
+ {"title":"Choice B","description":"Banana","emoji":"๐"},
+ {"title":"Choice C","description":"Orange","emoji":"๐"},
+ {"title":"Choice D","description":"Kiwi","emoji":"๐ฅ"}
+ ]
+ },
+ {
+ /* A radio select input modal question with up to 10 choices. */
+ "id":"example-question-4",
+ "name":"Example Question 4",
+ "description":"This is a radio select question.", //Leave empty to disable
+ "type":"radio-select",
+ "required":true,
+
+ "choices":[
+ {"title":"Choice A","description":"Up","selectedByDefault":false},
+ {"title":"Choice B","description":"Down","selectedByDefault":false},
+ {"title":"Choice C","description":"Left","selectedByDefault":false},
+ {"title":"Choice D","description":"Right","selectedByDefault":false}
+ ]
+ },
+ {
+ /* A checkbox select input modal question with up to 10 choices. */
+ "id":"example-question-5",
+ "name":"Example Question 5",
+ "description":"This is a checkbox select question.", //Leave empty to disable
+ "type":"checkbox-select",
+ "required":true,
+
+ "limits":{
+ /* Configure checkbox amount limits for the answer. */
+ "enabled":false,
+ "min":0,
+ "max":10
+ },
+ "choices":[
+ {"title":"Choice A","description":"Happiness","selectedByDefault":false},
+ {"title":"Choice B","description":"Anger","selectedByDefault":false},
+ {"title":"Choice C","description":"Sadness","selectedByDefault":false},
+ {"title":"Choice D","description":"Fear","selectedByDefault":false}
+ ]
+ },
+ {
+ /* Show text in a modal to provide extra details or explain questions. */
+ "id":"example-text-display",
+ "type":"text-display",
+
+ "textContents":"This is a text display. It isn't a question, but allows you to display additional details."
+ }
+]
\ No newline at end of file
diff --git a/config/transcripts.json b/config/transcripts.json
deleted file mode 100644
index bd01a59..0000000
--- a/config/transcripts.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "general":{
- "enabled":false,
-
- "enableChannel":false,
- "enableCreatorDM":false,
- "enableParticipantDM":false,
- "enableActiveAdminDM":false,
- "enableEveryAdminDM":false,
-
- "channel":"DISCORD_TRANSCRIPT_CHANNEL_ID (leave empty to disable)",
- "mode":"html OR text"
- },
- "embedSettings":{
- "customColor":"#f8ab00 (leave empty to disable)",
- "listAllParticipants":false,
- "includeTicketStats":false
- },
- "textTranscriptStyle":{
- "layout":"simple OR normal OR detailed",
- "includeStats":true,
- "includeIds":false,
- "includeEmbeds":true,
- "includeFiles":true,
- "includeBotMessages":true,
-
- "fileMode":"custom OR channel-name OR channel-id OR user-name OR user-id",
- "customFileName":"transcript"
- },
- "htmlTranscriptStyle":{
- "background":{
- "enableCustomBackground":false,
- "backgroundColor":"#f8ba00 (leave empty to disable)",
- "backgroundImage":"https://www.example.com/image.png (leave empty to disable)"
- },
- "header":{
- "enableCustomHeader":false,
- "backgroundColor":"#202225",
- "decoColor":"#f8ba00",
- "textColor":"#ffffff"
- },
- "stats":{
- "enableCustomStats":false,
- "backgroundColor":"#202225",
- "keyTextColor":"#737373",
- "valueTextColor":"#ffffff",
- "hideBackgroundColor":"#40444a",
- "hideTextColor":"#ffffff"
- },
- "favicon":{
- "enableCustomFavicon":false,
- "imageUrl":"https://t.dj-dj.be/favicon.png"
- }
- }
-}
\ No newline at end of file
diff --git a/config/transcripts.jsonc b/config/transcripts.jsonc
new file mode 100644
index 0000000..2a441c5
--- /dev/null
+++ b/config/transcripts.jsonc
@@ -0,0 +1,70 @@
+/*
+ * OPEN TICKET TRANSCRIPTS
+ * ----------------------------------------------
+ * Enable transcript creation when tickets are deleted. There are 2 available transcript types: HTML & Text
+ *
+ * HTML Transcripts (recommended):
+ * Generate transcripts as HTML files to view in the browser. No server or domain required. HTML Transcripts use an external service to process and host the transcripts.
+ *
+ * Text Transcripts:
+ * Generate transcripts as simple .txt files with limited details. Processing happens fully local.
+ */
+{
+ "general":{
+ "enabled":false,
+
+ /* Choose which users and channel get the generated transcript. */
+ "enableChannel":false,
+ "enableCreatorDM":false,
+ "enableParticipantDM":false,
+ "enableActiveAdminDM":false,
+ "enableEveryAdminDM":false,
+
+ "channel":"DISCORD_CHANNEL_ID", //Where to send transcripts. Leave empty when disabled.
+ "mode":"html" //The type of transcript to use. Choices: html, text
+ },
+ "embedSettings":{
+ /* Customise the embed which contains the generated transcript file or URL. */
+ "customColor":"#f8ab00", //Leave empty to use default color
+ "listAllParticipants":false,
+ "includeTicketStats":false
+ },
+ "textTranscriptStyle":{
+ /* Customise layout of the text transcripts. */
+ "layout":"normal", //Choices: simple, normal, detailed
+ "includeStats":true,
+ "includeIds":false,
+ "includeEmbeds":true,
+ "includeFiles":true,
+ "includeBotMessages":true,
+
+ "fileMode":"custom", //How to name the transcript file? Choices: custom, channel-name, channel-id, user-name, user-id
+ "customFileName":"transcript" //Custom filename without extension
+ },
+ "htmlTranscriptStyle":{
+ /* Customise layout of the HTML transcripts. */
+ "background":{
+ "enableCustomBackground":false,
+ "backgroundColor":"#f8ba00", //Leave empty to use Open Ticket color (#f8ba00)
+ "backgroundImage":"https://www.example.com/image.png" //Image URL to fill entire background. Leave empty to disable
+ },
+ "header":{
+ "enableCustomHeader":false,
+ "backgroundColor":"#202225",
+ "decoColor":"#f8ba00",
+ "textColor":"#ffffff"
+ },
+ "stats":{
+ "enableCustomStats":false,
+ "backgroundColor":"#202225",
+ "keyTextColor":"#737373",
+ "valueTextColor":"#ffffff",
+ "hideBackgroundColor":"#40444a",
+ "hideTextColor":"#ffffff"
+ },
+ "favicon":{
+ "enableCustomFavicon":false,
+ "imageUrl":"https://t.dj-dj.be/favicon.png"
+ }
+ }
+}
\ No newline at end of file
diff --git a/database/states.json b/database/states.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/database/states.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/index.js b/index.js
index 1c05e7d..2935154 100644
--- a/index.js
+++ b/index.js
@@ -13,7 +13,7 @@ const flags = [
โโโ โโโโโโโโโโ โโโโโโ โโโโโโโโโโ โโโ โโโโโโ โโโโโโโ โโโโโโ โโโ
โโโโโโโโโโโโ โโโโโโโโโโโ โโโโโโ โโโ โโโโโโโโโโโโโโ โโโโโโโโโโโ โโโ
โโโโโโโ โโโ โโโโโโโโโโโ โโโโโ โโโ โโโ โโโโโโโโโโ โโโโโโโโโโโ โโโ
- v4.1.3 - Made by DJj123dj & Contributors
+ v4.2.0 - Made by DJj123dj & Contributors
Discord: https://discord.dj-dj.be
Docs: https://otdocs.dj-dj.be
diff --git a/languages/arabic.json b/languages/arabic.json
index 694bf33..40c7cff 100644
--- a/languages/arabic.json
+++ b/languages/arabic.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["palestinian"],
"lastedited":"16/02/2026",
"language":"Arabic",
diff --git a/languages/bengali.json b/languages/bengali.json
index fe5894f..317f3d5 100644
--- a/languages/bengali.json
+++ b/languages/bengali.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta"],
"lastedited":"16/02/2026",
"language":"Bengali",
diff --git a/languages/catalan.json b/languages/catalan.json
index 1336d34..fed41c0 100644
--- a/languages/catalan.json
+++ b/languages/catalan.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["guillee3"],
"lastedited":"16/02/2026",
"language":"Catalan",
diff --git a/languages/custom.json b/languages/custom.json
index ce7acda..f22936f 100644
--- a/languages/custom.json
+++ b/languages/custom.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["DJj123dj"],
"lastedited":"16/02/2026",
"language":"Custom",
diff --git a/languages/czech.json b/languages/czech.json
index ef4a93a..03b8bc3 100644
--- a/languages/czech.json
+++ b/languages/czech.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["spyeye_"],
"lastedited":"16/02/2026",
"language":"Czech",
diff --git a/languages/danish.json b/languages/danish.json
index 4f93fed..ab42fd0 100644
--- a/languages/danish.json
+++ b/languages/danish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["the_gamer"],
"lastedited":"16/02/2026",
"language":"Danish",
diff --git a/languages/dutch.json b/languages/dutch.json
index f13ddfc..9c19866 100644
--- a/languages/dutch.json
+++ b/languages/dutch.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["DJj123dj"],
"lastedited":"16/02/2026",
"language":"Dutch",
diff --git a/languages/english.json b/languages/english.json
index ed96bb2..7244893 100644
--- a/languages/english.json
+++ b/languages/english.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["DJj123dj"],
"lastedited":"16/02/2026",
"language":"English",
diff --git a/languages/estonian.json b/languages/estonian.json
index 241bfec..1225d79 100644
--- a/languages/estonian.json
+++ b/languages/estonian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["iamnotmega","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Estonian",
diff --git a/languages/finnish.json b/languages/finnish.json
index d922108..895a1bf 100644
--- a/languages/finnish.json
+++ b/languages/finnish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["iamnotmega","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Finnish",
diff --git a/languages/french.json b/languages/french.json
index d50f971..38b582f 100644
--- a/languages/french.json
+++ b/languages/french.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["guillee3"],
"lastedited":"16/02/2026",
"language":"French",
diff --git a/languages/german.json b/languages/german.json
index 1a9b112..0c8e1c4 100644
--- a/languages/german.json
+++ b/languages/german.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["benzorich"],
"lastedited":"16/02/2026",
"language":"German",
diff --git a/languages/greek.json b/languages/greek.json
index 998e304..bc11230 100644
--- a/languages/greek.json
+++ b/languages/greek.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Greek",
diff --git a/languages/hindi.json b/languages/hindi.json
index 30dd621..0addf7e 100644
--- a/languages/hindi.json
+++ b/languages/hindi.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["challenger_nova"],
"lastedited":"16/02/2026",
"language":"Hindi",
diff --git a/languages/hungarian.json b/languages/hungarian.json
index bf989b3..7908d10 100644
--- a/languages/hungarian.json
+++ b/languages/hungarian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["Kornel0706"],
"lastedited":"16/02/2026",
"language":"Hungarian",
diff --git a/languages/indonesian.json b/languages/indonesian.json
index 66c1483..7ea791d 100644
--- a/languages/indonesian.json
+++ b/languages/indonesian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["erxg"],
"lastedited":"16/02/2026",
"language":"Indonesian",
diff --git a/languages/italian.json b/languages/italian.json
index 28a6723..e7be54b 100644
--- a/languages/italian.json
+++ b/languages/italian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["fraden1mvp.","imperatorix_17"],
"lastedited":"16/02/2026",
"language":"Italian",
diff --git a/languages/japanese.json b/languages/japanese.json
index f528a9a..ad91014 100644
--- a/languages/japanese.json
+++ b/languages/japanese.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Japanese",
diff --git a/languages/korean.json b/languages/korean.json
index 00e2d6b..17234c6 100644
--- a/languages/korean.json
+++ b/languages/korean.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Korean",
diff --git a/languages/kurdish.json b/languages/kurdish.json
index 0b14b78..be9547e 100644
--- a/languages/kurdish.json
+++ b/languages/kurdish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Kurdish",
diff --git a/languages/latvian.json b/languages/latvian.json
index 859e8ce..16755a1 100644
--- a/languages/latvian.json
+++ b/languages/latvian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Latvian",
diff --git a/languages/lithuanian.json b/languages/lithuanian.json
index ba21919..9c32861 100644
--- a/languages/lithuanian.json
+++ b/languages/lithuanian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["TsgIndrius"],
"lastedited":"16/02/2026",
"language":"Lithuanian",
diff --git a/languages/norwegian.json b/languages/norwegian.json
index 42b6ff5..a87a119 100644
--- a/languages/norwegian.json
+++ b/languages/norwegian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Norwegian",
diff --git a/languages/persian.json b/languages/persian.json
index f262ff9..2d79b24 100644
--- a/languages/persian.json
+++ b/languages/persian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["dysashop","zhavis"],
"lastedited":"16/02/2026",
"language":"Persian",
diff --git a/languages/polish.json b/languages/polish.json
index ae3bcc9..b30c1c0 100644
--- a/languages/polish.json
+++ b/languages/polish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["DanoGlez"],
"lastedited":"16/02/2026",
"language":"Polish",
diff --git a/languages/portuguese.json b/languages/portuguese.json
index 182a73d..3e3ad64 100644
--- a/languages/portuguese.json
+++ b/languages/portuguese.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["quiradon"],
"lastedited":"16/02/2026",
"language":"Portuguese",
diff --git a/languages/romanian.json b/languages/romanian.json
index 36e26ee..3521dc7 100644
--- a/languages/romanian.json
+++ b/languages/romanian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["SankeDev"],
"lastedited":"16/02/2026",
"language":"Romanian",
diff --git a/languages/russian.json b/languages/russian.json
index adc1081..16e833c 100644
--- a/languages/russian.json
+++ b/languages/russian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Russian",
diff --git a/languages/simplified-chinese.json b/languages/simplified-chinese.json
index 07457d7..4d1564c 100644
--- a/languages/simplified-chinese.json
+++ b/languages/simplified-chinese.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Simplified Chainese",
diff --git a/languages/slovenian.json b/languages/slovenian.json
index 6ec6988..305d3e4 100644
--- a/languages/slovenian.json
+++ b/languages/slovenian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Solvenian",
diff --git a/languages/spanish.json b/languages/spanish.json
index f9d7335..cf15d56 100644
--- a/languages/spanish.json
+++ b/languages/spanish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["Redactado","Josuens"],
"lastedited":"16/02/2026",
"language":"Spanish",
diff --git a/languages/swedish.json b/languages/swedish.json
index d71f506..15571cc 100644
--- a/languages/swedish.json
+++ b/languages/swedish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Svenska",
diff --git a/languages/tamil.json b/languages/tamil.json
index 0a52857..9b68dc7 100644
--- a/languages/tamil.json
+++ b/languages/tamil.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Tamil",
diff --git a/languages/thai.json b/languages/thai.json
index 71f1294..f2b4b6a 100644
--- a/languages/thai.json
+++ b/languages/thai.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["modshd"],
"lastedited":"16/02/2026",
"language":"Thai",
diff --git a/languages/traditional-chinese.json b/languages/traditional-chinese.json
index 21fd839..d44f21c 100644
--- a/languages/traditional-chinese.json
+++ b/languages/traditional-chinese.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["me.october"],
"lastedited":"10/03/2026",
"language":"Traditional Chinese",
diff --git a/languages/turkish.json b/languages/turkish.json
index ee66fc5..09e949b 100644
--- a/languages/turkish.json
+++ b/languages/turkish.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["palestinian"],
"lastedited":"16/02/2026",
"language":"Turkish",
diff --git a/languages/ukrainian.json b/languages/ukrainian.json
index 416da30..c1c2a19 100644
--- a/languages/ukrainian.json
+++ b/languages/ukrainian.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["Anderskiy"],
"lastedited":"16/02/2026",
"language":"Ukrainian",
diff --git a/languages/vietnamese.json b/languages/vietnamese.json
index 12d6318..24f43c0 100644
--- a/languages/vietnamese.json
+++ b/languages/vietnamese.json
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
- "otversion":"v4.1.3",
+ "otversion":"v4.2.0",
"translators":["ngocdiep2006"],
"lastedited":"16/02/2026",
"language":"Vietnamese",
diff --git a/package.json b/package.json
index 75330b8..027eed9 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "open-ticket",
"author": "DJdj Development",
- "version": "4.1.3",
+ "version": "4.2.0",
"description": "The most advanced open-source discord ticket bot with HTML transcripts, plugins, questions, claiming, pinning & more! Using discord.js v14 & JSON database! ",
"keywords": [
"ticket-bot",
@@ -26,12 +26,12 @@
"license": "GPL-3.0-only",
"dependencies": {
"@discordjs/rest": "^2.6.1",
- "@open-discord-bots/framework": "^0.4.0",
+ "@open-discord-bots/framework": "^0.4.2",
"@types/node": "^22.5.0",
"@types/terminal-kit": "^2.5.7",
"ansis": "^4.2.0",
- "discord.js": "^14.26.3",
- "formatted-json-stringify": "^1.3.1",
+ "discord.js": "^14.26.4",
+ "formatted-json-stringify": "^1.3.2",
"terminal-kit": "^3.1.2",
"typescript": "^6.0.3"
},
diff --git a/src/actions/addTicketUser.ts b/src/actions/addTicketUser.ts
index ac91b5c..27da7ce 100644
--- a/src/actions/addTicketUser.ts
+++ b/src/actions/addTicketUser.ts
@@ -49,14 +49,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason,data} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.adding.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.adding.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"add",reason,additionalData:data}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.adding.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"add",reason,additionalData:data}))
+ if (creator && generalConfig.data.logs.logMessages.adding.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"add",reason,additionalData:data}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,data} = params
diff --git a/src/actions/calculateTicketCategory.ts b/src/actions/calculateTicketCategory.ts
index cd75bb9..dcc2c49 100644
--- a/src/actions/calculateTicketCategory.ts
+++ b/src/actions/calculateTicketCategory.ts
@@ -46,8 +46,9 @@ export async function registerActions(){
const {guild,user,channel,option,ticket,currentCategoryId} = params
if (!ticket) return
if (!ticket.get("opendiscord:closed").value) return
+ if (!generalConfig.data.ticketSystem.closedCategory.enabled) return
- const closeCategoryId = option.get("opendiscord:channel-category-closed").value
+ const closeCategoryId = generalConfig.data.ticketSystem.closedCategory.categoryId
if (!closeCategoryId) return
const closeCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,closeCategoryId)
if (closeCategory){
@@ -74,7 +75,7 @@ export async function registerActions(){
if (!ticket) return
if (!ticket.get("opendiscord:claimed").value) return
- const claimedCategoryIds = option.get("opendiscord:channel-categories-claimed").value
+ const claimedCategoryIds = generalConfig.data.ticketSystem.claimedCategories
const claimCategoryId = claimedCategoryIds.find((c) => c.user == user.id)?.category
if (!claimCategoryId) return
const claimCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,claimCategoryId)
@@ -101,8 +102,9 @@ export async function registerActions(){
const {guild,user,channel,option,ticket,currentCategoryId} = params
if (!instance.newCategory || !instance.newCategoryId || !instance.shouldChangeCategory) return
if (instance.newCategory.children.cache.size < 50) return
+ if (!generalConfig.data.ticketSystem.backupCategory.enabled) return
- const backupCategoryId = option.get("opendiscord:channel-category-backup").value
+ const backupCategoryId = generalConfig.data.ticketSystem.backupCategory.categoryId
if (!backupCategoryId) return
const backupCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,backupCategoryId)
if (backupCategory){
diff --git a/src/actions/calculateTicketName.ts b/src/actions/calculateTicketName.ts
index 2c98def..f6d1777 100644
--- a/src/actions/calculateTicketName.ts
+++ b/src/actions/calculateTicketName.ts
@@ -19,7 +19,7 @@ export async function registerActions(){
const baseChannelName = (channelRenamed) ? channelRenamed : channelPrefix+channelSuffix
//calculate status emojis
- const pinEmoji = (ticket && ticket.get("opendiscord:pinned").value) ? generalConfig.data.system.pinEmoji : ""
+ const pinEmoji = (ticket && ticket.get("opendiscord:pinned").value) ? generalConfig.data.ticketSystem.pinEmoji : ""
const priorityEmoji = (ticket) ? (opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).channelEmoji ?? "") : ""
instance.newChannelName = pinEmoji+priorityEmoji+baseChannelName
diff --git a/src/actions/claimTicket.ts b/src/actions/claimTicket.ts
index b9ca1de..e6a8c79 100644
--- a/src/actions/claimTicket.ts
+++ b/src/actions/claimTicket.ts
@@ -73,14 +73,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.claiming.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.claiming.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"claim",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.claiming.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"claim",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.claiming.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"claim",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/clearTickets.ts b/src/actions/clearTickets.ts
index 4e75d1f..3c15e2b 100644
--- a/src/actions/clearTickets.ts
+++ b/src/actions/clearTickets.ts
@@ -47,7 +47,7 @@ export async function registerActions(){
const {guild,channel,user,filter,list} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.deleting.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.deleting.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:clear-logs").build(origin,{guild,channel,user,filter,list:instance.list ?? []}))
}
diff --git a/src/actions/closeTicket.ts b/src/actions/closeTicket.ts
index c405fc1..0c1e959 100644
--- a/src/actions/closeTicket.ts
+++ b/src/actions/closeTicket.ts
@@ -99,7 +99,7 @@ export async function registerActions(){
ticket.get("opendiscord:participants").value.forEach((participant) => {
//all participants that aren't roles/admins => readonly (OR non-viewable when enabled)
if (participant.type == "user"){
- if (generalConfig.data.system.removeParticipantsOnClose) permissions.push({
+ if (generalConfig.data.ticketSystem.removeParticipantsOnClose) permissions.push({
type:discord.OverwriteType.Member,
id:participant.id,
allow:[],
@@ -138,14 +138,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.closing.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.closing.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"close",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.closing.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"close",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.closing.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"close",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/createTicket.ts b/src/actions/createTicket.ts
index 68f2e89..c8ff9ca 100644
--- a/src/actions/createTicket.ts
+++ b/src/actions/createTicket.ts
@@ -91,15 +91,15 @@ export async function registerActions(){
//handle channel topic
const channelTopics: string[] = []
- if (generalConfig.data.system.channelTopic.showOptionName) channelTopics.push(option.get("opendiscord:name").value)
- if (generalConfig.data.system.channelTopic.showOptionDescription) channelTopics.push(option.get("opendiscord:description").value)
- if (generalConfig.data.system.channelTopic.showOptionTopic) channelTopics.push(channelTopicText)
- if (generalConfig.data.system.channelTopic.showPriority) channelTopics.push("**"+lang.getTranslation("params.uppercase.priority")+":** "+opendiscord.priorities.get("opendiscord:none").renderDisplayName())
- if (generalConfig.data.system.channelTopic.showClosed) channelTopics.push("**"+lang.getTranslation("params.uppercase.status")+":** "+lang.getTranslation("params.uppercase.open"))
- if (generalConfig.data.system.channelTopic.showClaimed) channelTopics.push("**"+lang.getTranslation("stats.properties.claimedBy")+":** "+lang.getTranslation("params.uppercase.noone"))
- if (generalConfig.data.system.channelTopic.showPinned) channelTopics.push("**"+lang.getTranslation("params.uppercase.pinned")+":** "+lang.getTranslation("params.uppercase.no"))
- if (generalConfig.data.system.channelTopic.showCreator) channelTopics.push("**"+lang.getTranslation("params.uppercase.creator")+":** "+discord.userMention(user.id))
- if (generalConfig.data.system.channelTopic.showParticipants) channelTopics.push("**"+lang.getTranslation("params.uppercase.participants")+":** "+participants.map((p) => (p.type == "user") ? discord.userMention(p.id) : discord.roleMention(p.id)).join(", "))
+ if (generalConfig.data.ticketSystem.channelTopic.showOptionName) channelTopics.push(option.get("opendiscord:name").value)
+ if (generalConfig.data.ticketSystem.channelTopic.showOptionDescription) channelTopics.push(option.get("opendiscord:description").value)
+ if (generalConfig.data.ticketSystem.channelTopic.showOptionTopic) channelTopics.push(channelTopicText)
+ if (generalConfig.data.ticketSystem.channelTopic.showPriority) channelTopics.push("**"+lang.getTranslation("params.uppercase.priority")+":** "+opendiscord.priorities.get("opendiscord:none").renderDisplayName())
+ if (generalConfig.data.ticketSystem.channelTopic.showClosed) channelTopics.push("**"+lang.getTranslation("params.uppercase.status")+":** "+lang.getTranslation("params.uppercase.open"))
+ if (generalConfig.data.ticketSystem.channelTopic.showClaimed) channelTopics.push("**"+lang.getTranslation("stats.properties.claimedBy")+":** "+lang.getTranslation("params.uppercase.noone"))
+ if (generalConfig.data.ticketSystem.channelTopic.showPinned) channelTopics.push("**"+lang.getTranslation("params.uppercase.pinned")+":** "+lang.getTranslation("params.uppercase.no"))
+ if (generalConfig.data.ticketSystem.channelTopic.showCreator) channelTopics.push("**"+lang.getTranslation("params.uppercase.creator")+":** "+discord.userMention(user.id))
+ if (generalConfig.data.ticketSystem.channelTopic.showParticipants) channelTopics.push("**"+lang.getTranslation("params.uppercase.participants")+":** "+participants.map((p) => (p.type == "user") ? discord.userMention(p.id) : discord.roleMention(p.id)).join(", "))
//create channel
const channel = await guild.channels.create({
@@ -192,7 +192,7 @@ export async function registerActions(){
ticket.get("opendiscord:ticket-message").value = ticketMsg.id
//pin ticket message (if required)
- if (generalConfig.data.system.pinFirstTicketMessage && ticketMsg.pinnable) await ticketMsg.pin("Ticket Message")
+ if (generalConfig.data.ticketSystem.pinFirstTicketMessage && ticketMsg.pinnable) await ticketMsg.pin("Ticket Message")
//manage stats
await opendiscord.statistics.get("opendiscord:ticket").setStat("opendiscord:messages-sent",ticket.id.value,1,"increase")
@@ -212,13 +212,13 @@ export async function registerActions(){
if (!ticket || !channel) return opendiscord.log("Ticket Creation Error: Unable to send ticket message. Previous worker failed!","error")
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.creation.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.creation.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created-logs").build(origin,{guild,channel,user,ticket}))
}
//to dm
- if (generalConfig.data.system.messages.creation.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:ticket-created-dm").build(origin,{guild,channel,user,ticket}))
+ if (generalConfig.data.logs.logMessages.creation.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:ticket-created-dm").build(origin,{guild,channel,user,ticket}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,user,answers,option} = params
diff --git a/src/actions/createTicketPermissions.ts b/src/actions/createTicketPermissions.ts
index 39daa1b..0617124 100644
--- a/src/actions/createTicketPermissions.ts
+++ b/src/actions/createTicketPermissions.ts
@@ -38,14 +38,14 @@ export async function registerActions(){
}),
new api.ODWorker("opendiscord:check-global-limits",2,(instance,params,origin,cancel) => {
const generalConfig = opendiscord.configs.get("opendiscord:general")
- if (!generalConfig.data.system.limits.enabled) return
+ if (!generalConfig.data.ticketSystem.limits.enabled) return
const allTickets = opendiscord.tickets.getAll()
const globalTicketCount = allTickets.length
const userTickets = opendiscord.tickets.getFiltered((ticket) => ticket.exists("opendiscord:opened-by") && (ticket.get("opendiscord:opened-by").value == params.user.id))
const userTicketCount = userTickets.length
- if (globalTicketCount >= generalConfig.data.system.limits.globalMaximum){
+ if (globalTicketCount >= generalConfig.data.ticketSystem.limits.globalMaximum){
instance.valid = false
instance.reason = "global-limit"
opendiscord.log(params.user.displayName+" tried to create a ticket but reached the limit!","info",[
@@ -55,7 +55,7 @@ export async function registerActions(){
{key:"limit",value:"global"}
])
return cancel()
- }else if (userTicketCount >= generalConfig.data.system.limits.userMaximum){
+ }else if (userTicketCount >= generalConfig.data.ticketSystem.limits.userMaximum){
instance.valid = false
instance.reason = "global-user-limit"
opendiscord.log(params.user.displayName+" tried to create a ticket, but reached the limit!","info",[
diff --git a/src/actions/deleteTicket.ts b/src/actions/deleteTicket.ts
index c6cc4ce..500ab61 100644
--- a/src/actions/deleteTicket.ts
+++ b/src/actions/deleteTicket.ts
@@ -60,14 +60,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.deleting.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.deleting.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"delete",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.deleting.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"delete",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.deleting.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"delete",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:delete-channel",1,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
diff --git a/src/actions/moveTicket.ts b/src/actions/moveTicket.ts
index 30ce42b..2679368 100644
--- a/src/actions/moveTicket.ts
+++ b/src/actions/moveTicket.ts
@@ -140,14 +140,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason,data} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.moving.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.moving.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"move",reason,additionalData:data}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.moving.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"move",reason,additionalData:data}))
+ if (creator && generalConfig.data.logs.logMessages.moving.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"move",reason,additionalData:data}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/pinTicket.ts b/src/actions/pinTicket.ts
index 551b0fd..6d52953 100644
--- a/src/actions/pinTicket.ts
+++ b/src/actions/pinTicket.ts
@@ -73,14 +73,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.pinning.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.pinning.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"pin",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.pinning.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"pin",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.pinning.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"pin",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/reactionRole.ts b/src/actions/reactionRole.ts
index 5460ecf..8a0a116 100644
--- a/src/actions/reactionRole.ts
+++ b/src/actions/reactionRole.ts
@@ -88,13 +88,13 @@ export async function registerActions(){
if (!instance.role || !instance.result) return
//to logs
- if (generalConfig.data.system.logs.enabled && (generalConfig.data.system.messages.reactionRole.logs)){
+ if (generalConfig.data.logs.enabled && (generalConfig.data.logs.logMessages.reactionRole.logs)){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:reaction-role-logs").build(origin,{guild,user,role:instance.role,result:instance.result}))
}
//to dm
- if (generalConfig.data.system.messages.reactionRole.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:reaction-role-dm").build(origin,{guild,user,role:instance.role,result:instance.result}))
+ if (generalConfig.data.logs.logMessages.reactionRole.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:reaction-role-dm").build(origin,{guild,user,role:instance.role,result:instance.result}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,user,option} = params
diff --git a/src/actions/removeTicketUser.ts b/src/actions/removeTicketUser.ts
index 5f91be8..93737fc 100644
--- a/src/actions/removeTicketUser.ts
+++ b/src/actions/removeTicketUser.ts
@@ -44,14 +44,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason,data} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.removing.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.removing.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"remove",reason,additionalData:data}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.removing.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"remove",reason,additionalData:data}))
+ if (creator && generalConfig.data.logs.logMessages.removing.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"remove",reason,additionalData:data}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,data} = params
diff --git a/src/actions/renameTicket.ts b/src/actions/renameTicket.ts
index ff552a1..7ffd071 100644
--- a/src/actions/renameTicket.ts
+++ b/src/actions/renameTicket.ts
@@ -52,14 +52,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason,data} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.renaming.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.renaming.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"rename",reason,additionalData:data}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.renaming.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"rename",reason,additionalData:data}))
+ if (creator && generalConfig.data.logs.logMessages.renaming.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"rename",reason,additionalData:data}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/reopenTicket.ts b/src/actions/reopenTicket.ts
index 361f9fc..75d06da 100644
--- a/src/actions/reopenTicket.ts
+++ b/src/actions/reopenTicket.ts
@@ -29,7 +29,7 @@ export async function registerActions(){
ticket.get("opendiscord:open").value = true
ticket.get("opendiscord:busy").value = true
- if (generalConfig.data.system.disableAutocloseAfterReopen){
+ if (generalConfig.data.ticketSystem.disableAutocloseAfterReopen){
//disable autoclose after reopen
ticket.get("opendiscord:autoclose-enabled").value = false
ticket.get("opendiscord:autoclose-hours").value = 0
@@ -136,14 +136,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.reopening.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.reopening.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"reopen",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.reopening.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"reopen",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.reopening.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"reopen",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/unclaimTicket.ts b/src/actions/unclaimTicket.ts
index 3f01cd8..e91b4ef 100644
--- a/src/actions/unclaimTicket.ts
+++ b/src/actions/unclaimTicket.ts
@@ -69,14 +69,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.claiming.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.claiming.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"unclaim",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.claiming.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"unclaim",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.claiming.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"unclaim",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/unpinTicket.ts b/src/actions/unpinTicket.ts
index b1299c5..747754a 100644
--- a/src/actions/unpinTicket.ts
+++ b/src/actions/unpinTicket.ts
@@ -64,14 +64,14 @@ export async function registerActions(){
const {guild,channel,user,ticket,reason} = params
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.pinning.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.pinning.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-logs").build(origin,{guild,channel,user,ticket,mode:"unpin",reason,additionalData:null}))
}
//to dm
const creator = await opendiscord.tickets.getTicketUser(ticket,"creator")
- if (creator && generalConfig.data.system.messages.pinning.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"unpin",reason,additionalData:null}))
+ if (creator && generalConfig.data.logs.logMessages.pinning.dm) await opendiscord.client.sendUserDm(creator,await opendiscord.builders.messages.getSafe("opendiscord:ticket-action-dm").build(origin,{guild,channel,user,ticket,mode:"unpin",reason,additionalData:null}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
diff --git a/src/actions/updateTicketTopic.ts b/src/actions/updateTicketTopic.ts
index 816cc1c..207a5d7 100644
--- a/src/actions/updateTicketTopic.ts
+++ b/src/actions/updateTicketTopic.ts
@@ -29,15 +29,15 @@ export async function registerActions(){
//handle channel topic
const channelTopics: string[] = []
- if (generalConfig.data.system.channelTopic.showOptionName) channelTopics.push(ticket.option.get("opendiscord:name").value)
- if (generalConfig.data.system.channelTopic.showOptionDescription) channelTopics.push(ticket.option.get("opendiscord:description").value)
- if (generalConfig.data.system.channelTopic.showOptionTopic) channelTopics.push(ticket.get("opendiscord:topic").value)
- if (generalConfig.data.system.channelTopic.showPriority) channelTopics.push("**"+lang.getTranslation("params.uppercase.priority")+":** "+opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).renderDisplayName())
- if (generalConfig.data.system.channelTopic.showClosed) channelTopics.push("**"+lang.getTranslation("params.uppercase.status")+":** "+(closed ? lang.getTranslation("params.uppercase.closed") : lang.getTranslation("params.uppercase.open")))
- if (generalConfig.data.system.channelTopic.showClaimed) channelTopics.push("**"+lang.getTranslation("stats.properties.claimedBy")+":** "+(claimedBy ? discord.userMention(claimedBy) : lang.getTranslation("params.uppercase.noone")))
- if (generalConfig.data.system.channelTopic.showPinned) channelTopics.push("**"+lang.getTranslation("params.uppercase.pinned")+":** "+(pinned ? lang.getTranslation("params.uppercase.yes") : lang.getTranslation("params.uppercase.no")))
- if (generalConfig.data.system.channelTopic.showCreator) channelTopics.push("**"+lang.getTranslation("params.uppercase.creator")+":** "+discord.userMention(creator))
- if (generalConfig.data.system.channelTopic.showParticipants) channelTopics.push("**"+lang.getTranslation("params.uppercase.participants")+":** "+ticket.get("opendiscord:participants").value.map((p) => (p.type == "user") ? discord.userMention(p.id) : discord.roleMention(p.id)).join(", "))
+ if (generalConfig.data.ticketSystem.channelTopic.showOptionName) channelTopics.push(ticket.option.get("opendiscord:name").value)
+ if (generalConfig.data.ticketSystem.channelTopic.showOptionDescription) channelTopics.push(ticket.option.get("opendiscord:description").value)
+ if (generalConfig.data.ticketSystem.channelTopic.showOptionTopic) channelTopics.push(ticket.get("opendiscord:topic").value)
+ if (generalConfig.data.ticketSystem.channelTopic.showPriority) channelTopics.push("**"+lang.getTranslation("params.uppercase.priority")+":** "+opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).renderDisplayName())
+ if (generalConfig.data.ticketSystem.channelTopic.showClosed) channelTopics.push("**"+lang.getTranslation("params.uppercase.status")+":** "+(closed ? lang.getTranslation("params.uppercase.closed") : lang.getTranslation("params.uppercase.open")))
+ if (generalConfig.data.ticketSystem.channelTopic.showClaimed) channelTopics.push("**"+lang.getTranslation("stats.properties.claimedBy")+":** "+(claimedBy ? discord.userMention(claimedBy) : lang.getTranslation("params.uppercase.noone")))
+ if (generalConfig.data.ticketSystem.channelTopic.showPinned) channelTopics.push("**"+lang.getTranslation("params.uppercase.pinned")+":** "+(pinned ? lang.getTranslation("params.uppercase.yes") : lang.getTranslation("params.uppercase.no")))
+ if (generalConfig.data.ticketSystem.channelTopic.showCreator) channelTopics.push("**"+lang.getTranslation("params.uppercase.creator")+":** "+discord.userMention(creator))
+ if (generalConfig.data.ticketSystem.channelTopic.showParticipants) channelTopics.push("**"+lang.getTranslation("params.uppercase.participants")+":** "+ticket.get("opendiscord:participants").value.map((p) => (p.type == "user") ? discord.userMention(p.id) : discord.roleMention(p.id)).join(", "))
//update channel
channel.setTopic(channelTopics.join(" โข "),"Topic Changed")
diff --git a/src/actions/utilities.ts b/src/actions/utilities.ts
index 4caa08b..8f87003 100644
--- a/src/actions/utilities.ts
+++ b/src/actions/utilities.ts
@@ -29,7 +29,7 @@ export async function replyHasPermissions(instance:api.ODButtonResponderInstance
//check permissions
const {user,member,channel,guild} = instance
const generalConfig = opendiscord.configs.get("opendiscord:general")
- const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions[commandName],"support",user,member,channel,guild,settings)
+ const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.permissions[commandName],"support",user,member,channel,guild,settings)
if (!permsResult.hasPerms){
if (permsResult.reason == "not-in-server" && channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
@@ -139,18 +139,18 @@ export async function replyMessageMustBeSentBeforeClose(instance:api.ODButtonRes
const {user,member,channel,guild} = instance
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
- const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions[commandName],"support",user,member,channel,guild)
+ const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.permissions[commandName],"support",user,member,channel,guild)
if (!permsResult.hasPerms) throw new api.ODSystemError("Please check permissions before using replyMessageMustBeSentBeforeClose()")
if (!guild || channel.isDMBased()) throw new api.ODSystemError("replyMessageMustBeSentBeforeClose() must be used in a guild and not a DM channel.")
- if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
+ if (!permsResult.isAdmin && (!generalConfig.data.ticketSystem.allowCloseBeforeMessage || !generalConfig.data.ticketSystem.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
- if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
+ if (analysis && !generalConfig.data.ticketSystem.allowCloseBeforeMessage && analysis.totalMessages < 1){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return false
}
- if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
+ if (analysis && !generalConfig.data.ticketSystem.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return false
}
diff --git a/src/builders/buttons.ts b/src/builders/buttons.ts
index db6d4ae..b4ae6cd 100644
--- a/src/builders/buttons.ts
+++ b/src/builders/buttons.ts
@@ -255,7 +255,7 @@ const ticketButtons = () => {
instance.setMode("button")
instance.setCustomId("od:pin-ticket")
instance.setColor("gray")
- instance.setEmoji(generalConfig.data.system.pinEmoji)
+ instance.setEmoji(generalConfig.data.ticketSystem.pinEmoji)
instance.setLabel(lang.getTranslation("actions.buttons.pin"))
})
)
@@ -269,7 +269,7 @@ const ticketButtons = () => {
instance.setMode("button")
instance.setCustomId("od:unpin-ticket")
instance.setColor("gray")
- instance.setEmoji(generalConfig.data.system.pinEmoji)
+ instance.setEmoji(generalConfig.data.ticketSystem.pinEmoji)
instance.setLabel(lang.getTranslation("actions.buttons.unpin"))
})
)
diff --git a/src/builders/embeds.ts b/src/builders/embeds.ts
index df686db..c8e7f7e 100644
--- a/src/builders/embeds.ts
+++ b/src/builders/embeds.ts
@@ -42,7 +42,7 @@ const errorEmbeds = () => {
const method = getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",customTitle ?? lang.getTranslation("errors.titles.internalError")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.internalError",[method]) + (layout == "simple") ? "\n"+error : "")
@@ -75,7 +75,7 @@ const errorEmbeds = () => {
})
const commandSyntax = "**"+error.prefix+error.name+" "+optionSyntax.join(" ")+"**"
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.optionMissing")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslation("errors.descriptions.optionMissing"))
@@ -130,7 +130,7 @@ const errorEmbeds = () => {
else if (error.reason == "channel_type" && error.option.type == "channel") reasonValue = lang.getTranslation("errors.optionInvalidReasons.channelType")
else if (error.reason == "not_in_guild") reasonValue = lang.getTranslation("errors.optionInvalidReasons.notInGuild")
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.optionInvalid")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslation("errors.descriptions.optionInvalid")+"\n"+reasonTitle+": `"+reasonValue+"`")
@@ -144,7 +144,7 @@ const errorEmbeds = () => {
new api.ODWorker("opendiscord:error-unknown-command",0,async (instance,params) => {
const {user} = params
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.unknownCommand")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslation("errors.descriptions.unknownCommand"))
@@ -169,7 +169,7 @@ const errorEmbeds = () => {
else if (perm == "discord-administrator") return "- "+lang.getTranslation("errors.permissions.discord-administrator")
}).join("\n")
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.noPermissions")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.noPermissions",[method]))
@@ -185,7 +185,7 @@ const errorEmbeds = () => {
const method = getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.noPermissions")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.noPermissionsCooldown",[method]))
@@ -201,7 +201,7 @@ const errorEmbeds = () => {
const method = getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.noPermissions")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.noPermissionsBlacklist",[method]))
@@ -214,7 +214,7 @@ const errorEmbeds = () => {
new api.ODWorker("opendiscord:error-no-permissions-limits",0,async (instance,params,origin) => {
const {user,limit} = params
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.noPermissions")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
if (limit == "global") instance.setDescription(lang.getTranslation("errors.descriptions.noPermissionsLimitGlobal"))
@@ -232,7 +232,7 @@ const errorEmbeds = () => {
const method = getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.internalError")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.internalError",[method]))
@@ -247,7 +247,7 @@ const errorEmbeds = () => {
new api.ODWorker("opendiscord:error-ticket-unknown",0,async (instance,params,origin) => {
const {user} = params
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.unknownTicket")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslation("errors.descriptions.unknownTicket"))
@@ -261,7 +261,7 @@ const errorEmbeds = () => {
new api.ODWorker("opendiscord:error-ticket-deprecated",0,async (instance,params,origin) => {
const {user} = params
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.deprecatedTicket")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslation("errors.descriptions.deprecatedTicket"))
@@ -281,7 +281,7 @@ const errorEmbeds = () => {
}else return "- `"+option.id.value+"`"
}).join("\n")
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.unknownOption")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setFooter(lang.getTranslation("errors.descriptions.askForInfo"))
@@ -301,7 +301,7 @@ const errorEmbeds = () => {
}else return "- `"+panel.id.value+"`"
}).join("\n")
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.unknownPanel")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.addFields({name:lang.getTranslation("params.uppercase.validPanels")+":",value:renderedPanels})
@@ -317,7 +317,7 @@ const errorEmbeds = () => {
const method = getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.notInGuild")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.notInGuild",[method]))
@@ -332,7 +332,7 @@ const errorEmbeds = () => {
const method = (origin == "ticket-move" || origin == "ticket-pin" || origin == "ticket-rename" || origin == "ticket-unpin" || origin == "ticket-priority" || origin == "ticket-transfer") ? origin : getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.channelRename")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslation("errors.descriptions.channelRename"))
@@ -352,7 +352,7 @@ const errorEmbeds = () => {
const method = (origin == "ticket-create" || origin == "ticket-close" || origin == "ticket-reopen" || origin == "ticket-claim" || origin == "ticket-unclaim" || origin == "ticket-move") ? origin : getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
//TODO TRANSLATION!!!
instance.setTitle(utilities.emojiTitle("โ","Unable To Change Category"))
instance.setAuthor(user.displayName,user.displayAvatarURL())
@@ -376,7 +376,7 @@ const errorEmbeds = () => {
const method = getMethodFromOrigin(origin)
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.busy")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("errors.descriptions.busy",[method]))
@@ -501,7 +501,7 @@ const statsEmbeds = () => {
new api.ODWorker("opendiscord:stats-ticket-unknown",0,async (instance,params) => {
const {user,id} = params
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("errors.titles.unknownTicket")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.statsError",[discord.channelMention(id)]))
@@ -534,8 +534,8 @@ const panelEmbeds = () => {
instance.setDescription(embedOptions.description)
}
- if (panel.get("opendiscord:enable-max-tickets-warning-embed").value && generalConfig.data.system.limits.enabled){
- instance.setDescription(instance.data.description+"\n\n*"+lang.getTranslationWithParams("actions.descriptions.ticketMessageLimit",[generalConfig.data.system.limits.userMaximum.toString()])+"*")
+ if (panel.get("opendiscord:enable-max-tickets-warning-embed").value && generalConfig.data.ticketSystem.limits.enabled){
+ instance.setDescription(instance.data.description+"\n\n*"+lang.getTranslationWithParams("actions.descriptions.ticketMessageLimit",[generalConfig.data.ticketSystem.limits.userMaximum.toString()])+"*")
}
if (panel.get("opendiscord:describe-options-in-embed-fields").value){
@@ -623,12 +623,12 @@ const ticketEmbeds = () => {
if (ticket.option.get("opendiscord:questions").value.length > 0){
//show config fields if mixing is allowed
- if (generalConfig.data.system.displayFieldsWithQuestions) instance.addFields(...embedOptions.fields)
+ if (generalConfig.data.ticketSystem.displayFieldsWithQuestions) instance.addFields(...embedOptions.fields)
const answers = ticket.get("opendiscord:answers").value
answers.forEach((answer) => {
if (!answer.value || answer.value.length == 0) return
- if (generalConfig.data.system.questionFieldsInCodeBlock) instance.addFields({name:answer.name,value:"```"+answer.value.slice(0,1024-6)+"```",inline:false})
+ if (generalConfig.data.ticketSystem.questionFieldsInCodeBlock) instance.addFields({name:answer.name,value:"```"+answer.value.slice(0,1024-6)+"```",inline:false})
else instance.addFields({name:answer.name,value:answer.value,inline:false})
})
}else if (embedOptions.fields){
@@ -656,7 +656,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.close")))
instance.setDescription(lang.getTranslation("actions.descriptions.close"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -670,7 +670,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.reopen")))
instance.setDescription(lang.getTranslation("actions.descriptions.reopen"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -684,7 +684,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐๏ธ",lang.getTranslation("actions.titles.delete")))
instance.setDescription(lang.getTranslation("actions.descriptions.delete"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -698,7 +698,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.claim")))
instance.setDescription(lang.getTranslation("actions.descriptions.claim"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -712,7 +712,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โฉ๏ธ",lang.getTranslation("actions.titles.unclaim")))
instance.setDescription(lang.getTranslation("actions.descriptions.unclaim"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -724,9 +724,9 @@ const ticketEmbeds = () => {
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
- instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.pin")))
+ instance.setTitle(utilities.emojiTitle(generalConfig.data.ticketSystem.pinEmoji,lang.getTranslation("actions.titles.pin")))
instance.setDescription(lang.getTranslation("actions.descriptions.pin"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -738,9 +738,9 @@ const ticketEmbeds = () => {
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
- instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.unpin")))
+ instance.setTitle(utilities.emojiTitle(generalConfig.data.ticketSystem.pinEmoji,lang.getTranslation("actions.titles.unpin")))
instance.setDescription(lang.getTranslation("actions.descriptions.unpin"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -754,7 +754,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.rename")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.rename",["`#"+data+"`"]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -768,7 +768,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.move")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.move",["`"+data.get("opendiscord:name").value+"`"]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -782,7 +782,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐ค",lang.getTranslation("actions.titles.add")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.add",[discord.userMention(data.id)]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -796,7 +796,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐ค",lang.getTranslation("actions.titles.remove")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.remove",[discord.userMention(data.id)]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -828,10 +828,10 @@ const ticketEmbeds = () => {
instance.setTitle(utilities.emojiTitle("โฉ๏ธ",lang.getTranslation("actions.titles.unclaim")))
instance.setDescription(lang.getTranslation("actions.logs.unclaimDm"))
}else if (mode == "pin"){
- instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.pin")))
+ instance.setTitle(utilities.emojiTitle(generalConfig.data.ticketSystem.pinEmoji,lang.getTranslation("actions.titles.pin")))
instance.setDescription(lang.getTranslation("actions.logs.pinDm"))
}else if (mode == "unpin"){
- instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.unpin")))
+ instance.setTitle(utilities.emojiTitle(generalConfig.data.ticketSystem.pinEmoji,lang.getTranslation("actions.titles.unpin")))
instance.setDescription(lang.getTranslation("actions.logs.unpinDm"))
}else if (mode == "rename"){
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.rename")))
@@ -864,7 +864,7 @@ const ticketEmbeds = () => {
{name:lang.getTranslation("params.uppercase.ticket")+":",value:"```#"+(channel ? channel.name : "")+"```",inline:false},
{name:lang.getTranslation("params.uppercase.option")+":",value:"```"+(ticket.option.get("opendiscord:name").value)+"```",inline:false},
)
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```",inline:false})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```",inline:false})
if (mode == "close"){
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.close")))
@@ -882,10 +882,10 @@ const ticketEmbeds = () => {
instance.setTitle(utilities.emojiTitle("โฉ๏ธ",lang.getTranslation("actions.titles.unclaim")))
instance.setDescription(lang.getTranslationWithParams("actions.logs.unclaimLog",[discord.userMention(user.id)]))
}else if (mode == "pin"){
- instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.pin")))
+ instance.setTitle(utilities.emojiTitle(generalConfig.data.ticketSystem.pinEmoji,lang.getTranslation("actions.titles.pin")))
instance.setDescription(lang.getTranslationWithParams("actions.logs.pinLog",[discord.userMention(user.id)]))
}else if (mode == "unpin"){
- instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.unpin")))
+ instance.setTitle(utilities.emojiTitle(generalConfig.data.ticketSystem.pinEmoji,lang.getTranslation("actions.titles.unpin")))
instance.setDescription(lang.getTranslationWithParams("actions.logs.unpinLog",[discord.userMention(user.id)]))
}else if (mode == "rename"){
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.rename")))
@@ -938,7 +938,7 @@ const blacklistEmbeds = () => {
if (blacklist){
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.blacklistGetSuccess",[discord.userMention(data.id)]))
- if (blacklist.reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(blacklist.reason ?? "/")+"```"})
+ if (blacklist.reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(blacklist.reason ?? "/")+"```"})
}else instance.setDescription("*"+lang.getTranslationWithParams("actions.descriptions.blacklistGetEmpty",[discord.userMention(data.id)])+"*")
})
@@ -954,7 +954,7 @@ const blacklistEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐ซ",lang.getTranslation("actions.titles.blacklistAdd")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.blacklistAdd",[discord.userMention(data.id)]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -968,7 +968,7 @@ const blacklistEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.blacklistRemove")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.blacklistRemove",[discord.userMention(data.id)]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -985,7 +985,7 @@ const blacklistEmbeds = () => {
instance.setTitle(utilities.emojiTitle((mode == "add") ? "๐ซ" : "๐",title))
instance.setTimestamp(new Date())
instance.setDescription(text)
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1004,7 +1004,7 @@ const blacklistEmbeds = () => {
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setTimestamp(new Date())
instance.setDescription(text)
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
@@ -1103,12 +1103,12 @@ const transcriptEmbeds = () => {
new api.ODWorker("opendiscord:transcript-error",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,compiler,reason} = params
- instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
+ instance.setColor(generalConfig.data.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โ",lang.getTranslation("transcripts.errors.title")))
instance.setTimestamp(new Date())
instance.setDescription(lang.getTranslation("transcripts.errors.error"))
instance.setFooter(lang.getTranslation("errors.descriptions.askForInfo"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
@@ -1289,7 +1289,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โฑ๏ธ",lang.getTranslation("actions.titles.autocloseEnabled")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.autocloseEnabled",[time.toString()]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1303,7 +1303,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โฑ๏ธ",lang.getTranslation("actions.titles.autodeleteEnabled")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.autodeleteEnabled",[time.toString()]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1317,7 +1317,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โฑ๏ธ",lang.getTranslation("actions.titles.autocloseDisabled")))
instance.setDescription(lang.getTranslation("actions.descriptions.autocloseDisabled"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1331,7 +1331,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("โฑ๏ธ",lang.getTranslation("actions.titles.autodeleteDisabled")))
instance.setDescription(lang.getTranslation("actions.descriptions.autodeleteDisabled"))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
@@ -1361,7 +1361,7 @@ const extraEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐จ",lang.getTranslation("actions.titles.prioritySet")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.prioritySet",["**"+priority.renderDisplayName()+"**",discord.userMention(user.id)]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1388,7 +1388,7 @@ const extraEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("๐",lang.getTranslation("actions.titles.transfer")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.transfer",[discord.userMention(oldCreator.id),discord.userMention(newCreator.id),discord.userMention(user.id)]))
- if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
+ if (reason || generalConfig.data.ticketSystem.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
\ No newline at end of file
diff --git a/src/builders/messages.ts b/src/builders/messages.ts
index e974882..7b49b23 100644
--- a/src/builders/messages.ts
+++ b/src/builders/messages.ts
@@ -286,8 +286,8 @@ const panelMessages = () => {
instance.setContent(text)
}
- if (panel.get("opendiscord:enable-max-tickets-warning-text").value && generalConfig.data.system.limits.enabled){
- instance.setContent(instance.data.content+"\n\n*"+lang.getTranslationWithParams("actions.descriptions.ticketMessageLimit",[generalConfig.data.system.limits.userMaximum.toString()])+"*")
+ if (panel.get("opendiscord:enable-max-tickets-warning-text").value && generalConfig.data.ticketSystem.limits.enabled){
+ instance.setContent(instance.data.content+"\n\n*"+lang.getTranslationWithParams("actions.descriptions.ticketMessageLimit",[generalConfig.data.ticketSystem.limits.userMaximum.toString()])+"*")
}
//add embed
@@ -399,7 +399,7 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:ticket-message-components",1,async (instance,params,origin) => {
const {guild,channel,user,ticket} = params
//add components
- if (generalConfig.data.system.enableTicketClaimButtons && !ticket.get("opendiscord:closed").value){
+ if (generalConfig.data.ticketSystem.enableTicketClaimButtons && !ticket.get("opendiscord:closed").value){
//enable ticket claiming
if (ticket.get("opendiscord:claimed").value){
instance.addComponent(await buttons.getSafe("opendiscord:unclaim-ticket").build("ticket-message",{guild,channel,user,ticket}))
@@ -407,7 +407,7 @@ const ticketMessages = () => {
instance.addComponent(await buttons.getSafe("opendiscord:claim-ticket").build("ticket-message",{guild,channel,user,ticket}))
}
}
- if (generalConfig.data.system.enableTicketPinButtons && !ticket.get("opendiscord:closed").value){
+ if (generalConfig.data.ticketSystem.enableTicketPinButtons && !ticket.get("opendiscord:closed").value){
//enable ticket pinning
if (ticket.get("opendiscord:pinned").value){
instance.addComponent(await buttons.getSafe("opendiscord:unpin-ticket").build("ticket-message",{guild,channel,user,ticket}))
@@ -415,7 +415,7 @@ const ticketMessages = () => {
instance.addComponent(await buttons.getSafe("opendiscord:pin-ticket").build("ticket-message",{guild,channel,user,ticket}))
}
}
- if (generalConfig.data.system.enableTicketCloseButtons){
+ if (generalConfig.data.ticketSystem.enableTicketCloseButtons){
//enable ticket closing
if (ticket.get("opendiscord:closed").value){
instance.addComponent(await buttons.getSafe("opendiscord:reopen-ticket").build("ticket-message",{guild,channel,user,ticket}))
@@ -424,7 +424,7 @@ const ticketMessages = () => {
}
}
//enable ticket deletion
- if (generalConfig.data.system.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("ticket-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("ticket-message",{guild,channel,user,ticket}))
}),
new api.ODWorker("opendiscord:ticket-message-disable-components",2,async (instance,params,origin) => {
const {ticket} = params
@@ -445,8 +445,8 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:close-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:close-message").build(origin,{guild,channel,user,ticket,reason}))
- if (generalConfig.data.system.enableTicketCloseButtons) instance.addComponent(await buttons.getSafe("opendiscord:reopen-ticket").build("close-message",{guild,channel,user,ticket}))
- if (generalConfig.data.system.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("close-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketCloseButtons) instance.addComponent(await buttons.getSafe("opendiscord:reopen-ticket").build("close-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("close-message",{guild,channel,user,ticket}))
})
)
@@ -456,8 +456,8 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:reopen-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:reopen-message").build(origin,{guild,channel,user,ticket,reason}))
- if (generalConfig.data.system.enableTicketCloseButtons) instance.addComponent(await buttons.getSafe("opendiscord:close-ticket").build("reopen-message",{guild,channel,user,ticket}))
- if (generalConfig.data.system.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("reopen-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketCloseButtons) instance.addComponent(await buttons.getSafe("opendiscord:close-ticket").build("reopen-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("reopen-message",{guild,channel,user,ticket}))
})
)
@@ -476,7 +476,7 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:claim-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:claim-message").build(origin,{guild,channel,user,ticket,reason}))
- if (generalConfig.data.system.enableTicketClaimButtons) instance.addComponent(await buttons.getSafe("opendiscord:unclaim-ticket").build("claim-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketClaimButtons) instance.addComponent(await buttons.getSafe("opendiscord:unclaim-ticket").build("claim-message",{guild,channel,user,ticket}))
})
)
@@ -486,7 +486,7 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:unclaim-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:unclaim-message").build(origin,{guild,channel,user,ticket,reason}))
- if (generalConfig.data.system.enableTicketClaimButtons) instance.addComponent(await buttons.getSafe("opendiscord:claim-ticket").build("unclaim-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketClaimButtons) instance.addComponent(await buttons.getSafe("opendiscord:claim-ticket").build("unclaim-message",{guild,channel,user,ticket}))
})
)
@@ -496,7 +496,7 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:pin-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:pin-message").build(origin,{guild,channel,user,ticket,reason}))
- if (generalConfig.data.system.enableTicketPinButtons) instance.addComponent(await buttons.getSafe("opendiscord:unpin-ticket").build("pin-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketPinButtons) instance.addComponent(await buttons.getSafe("opendiscord:unpin-ticket").build("pin-message",{guild,channel,user,ticket}))
})
)
@@ -506,7 +506,7 @@ const ticketMessages = () => {
new api.ODWorker("opendiscord:unpin-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:unpin-message").build(origin,{guild,channel,user,ticket,reason}))
- if (generalConfig.data.system.enableTicketPinButtons) instance.addComponent(await buttons.getSafe("opendiscord:pin-ticket").build("unpin-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketPinButtons) instance.addComponent(await buttons.getSafe("opendiscord:pin-ticket").build("unpin-message",{guild,channel,user,ticket}))
})
)
@@ -732,8 +732,8 @@ const autoMessages = () => {
new api.ODWorker("opendiscord:autoclose-message",0,async (instance,params,origin) => {
const {guild,channel,user,ticket} = params
instance.addEmbed(await embeds.getSafe("opendiscord:autoclose-message").build(origin,{guild,channel,user,ticket}))
- if (generalConfig.data.system.enableTicketCloseButtons) instance.addComponent(await buttons.getSafe("opendiscord:reopen-ticket").build("autoclose-message",{guild,channel,user,ticket}))
- if (generalConfig.data.system.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("autoclose-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketCloseButtons) instance.addComponent(await buttons.getSafe("opendiscord:reopen-ticket").build("autoclose-message",{guild,channel,user,ticket}))
+ if (generalConfig.data.ticketSystem.enableTicketDeleteButtons) instance.addComponent(await buttons.getSafe("opendiscord:delete-ticket").build("autoclose-message",{guild,channel,user,ticket}))
})
)
diff --git a/src/commands/blacklist.ts b/src/commands/blacklist.ts
index 7f445fc..10d0d18 100644
--- a/src/commands/blacklist.ts
+++ b/src/commands/blacklist.ts
@@ -14,7 +14,7 @@ export async function registerCommandResponders(){
const {guild,channel,user,member} = instance
//check permissions
- const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.blacklist,"support",user,member,channel,guild)
+ const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.permissions.blacklist,"support",user,member,channel,guild)
if (!permsResult.hasPerms){
if (permsResult.reason == "not-in-server") await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
else await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build(origin,{guild,channel,user,permissions:["support"]}))
@@ -83,13 +83,13 @@ export async function registerCommandResponders(){
const reason = instance.options.getString("reason",false)
//to logs
- if (generalConfig.data.system.logs.enabled && generalConfig.data.system.messages.blacklisting.logs){
+ if (generalConfig.data.logs.enabled && generalConfig.data.logs.logMessages.blacklisting.logs){
const logChannel = opendiscord.posts.get("opendiscord:logs")
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:blacklist-logs").build(origin,{guild,channel,user,mode:scope,data,reason}))
}
//to dm
- if (generalConfig.data.system.messages.blacklisting.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:blacklist-dm").build(origin,{guild,channel,user,mode:scope,data,reason}))
+ if (generalConfig.data.logs.logMessages.blacklisting.dm) await opendiscord.client.sendUserDm(user,await opendiscord.builders.messages.getSafe("opendiscord:blacklist-dm").build(origin,{guild,channel,user,mode:scope,data,reason}))
}),
new api.ODWorker("opendiscord:logs",-1,(instance,params,origin,cancel) => {
const scope = instance.options.getSubCommand()
diff --git a/src/commands/claim.ts b/src/commands/claim.ts
index 38abf62..555e573 100644
--- a/src/commands/claim.ts
+++ b/src/commands/claim.ts
@@ -90,7 +90,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
diff --git a/src/commands/close.ts b/src/commands/close.ts
index 9d60434..64f9595 100644
--- a/src/commands/close.ts
+++ b/src/commands/close.ts
@@ -95,7 +95,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
diff --git a/src/commands/delete.ts b/src/commands/delete.ts
index 6b06348..8c153a8 100644
--- a/src/commands/delete.ts
+++ b/src/commands/delete.ts
@@ -36,7 +36,7 @@ export async function registerCommandResponders(){
//don't allow deleteWithoutTranscript to non-global-admins when enabled
const withoutTranscript = instance.options.getBoolean("notranscript",false) ?? false
- if (withoutTranscript && generalConfig.data.system.adminOnlyDeleteWithoutTranscript){
+ if (withoutTranscript && generalConfig.data.ticketSystem.adminOnlyDeleteWithoutTranscript){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild,{allowChannelRoleScope:false,allowChannelUserScope:false,allowGlobalRoleScope:true,allowGlobalUserScope:true}))){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:["support"]}))
return cancel()
@@ -93,7 +93,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
@@ -146,7 +146,7 @@ export async function registerVerifyBars(){
//don't allow deleteWithoutTranscript to non-global-admins when enabled
const withoutTranscript = (params.selectedButtonId == "accept-without-transcript")
- if (withoutTranscript && generalConfig.data.system.adminOnlyDeleteWithoutTranscript){
+ if (withoutTranscript && generalConfig.data.ticketSystem.adminOnlyDeleteWithoutTranscript){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild,{allowChannelRoleScope:false,allowChannelUserScope:false,allowGlobalRoleScope:true,allowGlobalUserScope:true}))){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:["support"]}))
return cancel()
diff --git a/src/commands/help.ts b/src/commands/help.ts
index b852a3b..1ad49a2 100644
--- a/src/commands/help.ts
+++ b/src/commands/help.ts
@@ -19,7 +19,7 @@ export async function registerCommandResponders(){
//calculate slash/text mode for help menu
let mode: "slash"|"text"
- if (generalConfig.data.slashCommands && generalConfig.data.textCommands) mode = (generalConfig.data.system.preferSlashOverText) ? "slash" : "text"
+ if (generalConfig.data.slashCommands && generalConfig.data.textCommands) mode = (generalConfig.data.ticketSystem.preferSlashOverText) ? "slash" : "text"
else if (!generalConfig.data.slashCommands) mode = "text"
else mode = "slash"
diff --git a/src/commands/pin.ts b/src/commands/pin.ts
index 69def53..8ca9fc0 100644
--- a/src/commands/pin.ts
+++ b/src/commands/pin.ts
@@ -89,7 +89,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
diff --git a/src/commands/reopen.ts b/src/commands/reopen.ts
index 94f7af1..2ba0772 100644
--- a/src/commands/reopen.ts
+++ b/src/commands/reopen.ts
@@ -89,7 +89,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
diff --git a/src/commands/role.ts b/src/commands/role.ts
index 607125b..58dbc17 100644
--- a/src/commands/role.ts
+++ b/src/commands/role.ts
@@ -34,7 +34,7 @@ export async function registerButtonResponders(){
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel:instance.channel,user,error:"Unable to receive role update data from worker!",layout:"advanced"}))
return cancel()
}
- if (generalConfig.data.system.replyOnReactionRole) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:reaction-role").build("panel-button",{guild,user,role:res.role,result:res.result}))
+ if (generalConfig.data.ticketSystem.replyOnReactionRole) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:reaction-role").build("panel-button",{guild,user,role:res.role,result:res.result}))
})
)
}
\ No newline at end of file
diff --git a/src/commands/ticket.ts b/src/commands/ticket.ts
index 719d523..92c1c86 100644
--- a/src/commands/ticket.ts
+++ b/src/commands/ticket.ts
@@ -105,7 +105,7 @@ export async function registerButtonResponders(){
if (!(await checkTicketCreationPerms(instance,"panel-button",guild,user,option))) return cancel()
//CREATE TICKET
- await instance.defer((generalConfig.data.system.replyOnTicketCreation) ? "reply" : "update",true)
+ await instance.defer((generalConfig.data.ticketSystem.replyOnTicketCreation) ? "reply" : "update",true)
const res = await opendiscord.actions.get("opendiscord:create-ticket").run("panel-button",{guild,user,answers:[],option})
if (!res.channel || !res.ticket){
@@ -113,7 +113,7 @@ export async function registerButtonResponders(){
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel:instance.channel,user,error:"Unable to receive ticket or channel from callback! #1",layout:"advanced"}))
return cancel()
}
- if (generalConfig.data.system.replyOnTicketCreation) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build("panel-button",{guild,channel:res.channel,user,ticket:res.ticket}))
+ if (generalConfig.data.ticketSystem.replyOnTicketCreation) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build("panel-button",{guild,channel:res.channel,user,ticket:res.ticket}))
}
})
)
@@ -147,7 +147,7 @@ export async function registerDropdownResponders(){
if (!(await checkTicketCreationPerms(instance,"panel-dropdown",guild,user,option))) return cancel()
//CREATE TICKET
- await instance.defer((generalConfig.data.system.replyOnTicketCreation) ? "reply" : "update",true)
+ await instance.defer((generalConfig.data.ticketSystem.replyOnTicketCreation) ? "reply" : "update",true)
const res = await opendiscord.actions.get("opendiscord:create-ticket").run("panel-dropdown",{guild,user,answers:[],option})
if (!res.channel || !res.ticket){
@@ -155,7 +155,7 @@ export async function registerDropdownResponders(){
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel:instance.channel,user,error:"Unable to receive ticket or channel from callback! #1",layout:"advanced"}))
return cancel()
}
- if (generalConfig.data.system.replyOnTicketCreation) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build("panel-dropdown",{guild,channel:res.channel,user,ticket:res.ticket}))
+ if (generalConfig.data.ticketSystem.replyOnTicketCreation) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build("panel-dropdown",{guild,channel:res.channel,user,ticket:res.ticket}))
}
//update panel after dropdown usage (reset panel choice)
@@ -214,7 +214,7 @@ export async function registerModalResponders(){
}
})
- await instance.defer((generalConfig.data.system.replyOnTicketCreation) ? "reply" : "update",true)
+ await instance.defer((generalConfig.data.ticketSystem.replyOnTicketCreation) ? "reply" : "update",true)
//CREATE TICKET
const res = await opendiscord.actions.get("opendiscord:create-ticket").run(originalOrigin,{guild,user,answers,option})
@@ -223,7 +223,7 @@ export async function registerModalResponders(){
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:"Unable to receive ticket or channel from callback! #2",layout:"advanced"}))
return cancel()
}
- if (generalConfig.data.system.replyOnTicketCreation) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build(originalOrigin,{guild,channel:res.channel,user,ticket:res.ticket}))
+ if (generalConfig.data.ticketSystem.replyOnTicketCreation) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:ticket-created").build(originalOrigin,{guild,channel:res.channel,user,ticket:res.ticket}))
})
])
}
\ No newline at end of file
diff --git a/src/commands/unclaim.ts b/src/commands/unclaim.ts
index 13014b9..6250061 100644
--- a/src/commands/unclaim.ts
+++ b/src/commands/unclaim.ts
@@ -89,7 +89,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
diff --git a/src/commands/unpin.ts b/src/commands/unpin.ts
index 30cd988..a49d772 100644
--- a/src/commands/unpin.ts
+++ b/src/commands/unpin.ts
@@ -89,7 +89,7 @@ export async function registerButtonResponders(){
const originalMsgType = state.data.messageType
//send verifybar
- if (generalConfig.data.system.disableVerifyBars){
+ if (generalConfig.data.ticketSystem.disableVerifyBars){
//verifybar disabled, directly run response
await verifybar.activate(instance,"accept")
diff --git a/src/components/verifybarModifiers.ts b/src/components/verifybarModifiers.ts
index 67c9115..60d35ed 100644
--- a/src/components/verifybarModifiers.ts
+++ b/src/components/verifybarModifiers.ts
@@ -42,7 +42,7 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or close with reason
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.close"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
}))
//REOPEN TICKET VERIFYBAR
@@ -57,7 +57,7 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or reopen with reason
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.reopen"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
}))
//DELETE TICKET VERIFYBAR
@@ -72,8 +72,8 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or delete with reason or without transcript
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.delete"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
- if (generalConfig.data.system.enableDeleteWithoutTranscript) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithoutTranscript,lang.getTranslation("actions.buttons.withoutTranscript"),"red","๐")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableDeleteWithoutTranscript) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithoutTranscript,lang.getTranslation("actions.buttons.withoutTranscript"),"red","๐")
}))
//CLAIM TICKET VERIFYBAR
@@ -88,7 +88,7 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or claim with reason
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.claim"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
}))
//UNCLAIM TICKET VERIFYBAR
@@ -103,7 +103,7 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or unclaim with reason
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.unclaim"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
}))
//PIN TICKET VERIFYBAR
@@ -118,7 +118,7 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or pin with reason
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.pin"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
}))
//UNPIN TICKET VERIFYBAR
@@ -133,6 +133,6 @@ export async function registerAllVerifyBarModifiers(){
//cancel, accept or unpin with reason
await addVerifyButton(instance,params,"โ",true,api.ODVerifyButtonId.Cancel)
await addVerifyButton(instance,params,"โ
",true,api.ODVerifyButtonId.Accept,lang.getTranslation("actions.buttons.unpin"))
- if (generalConfig.data.system.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
+ if (generalConfig.data.ticketSystem.enableTicketActionWithReason) await addVerifyButton(instance,params,"โ
",false,api.ODVerifyButtonId.AcceptWithReason,lang.getTranslation("actions.buttons.withReason"),"blue","โ๏ธ")
}))
}
\ No newline at end of file
diff --git a/src/core/api/option.ts b/src/core/api/option.ts
index fa70697..b5034a6 100644
--- a/src/core/api/option.ts
+++ b/src/core/api/option.ts
@@ -32,9 +32,6 @@ export interface ODTicketOptionIdMappings extends ODOptionIdConstraint {
"opendiscord:channel-prefix":ODOptionData,
"opendiscord:channel-suffix":ODOptionData<"user-name"|"user-nickname"|"user-id"|"random-number"|"random-hex"|"counter-dynamic"|"counter-fixed">,
"opendiscord:channel-category":ODOptionData,
- "opendiscord:channel-category-closed":ODOptionData,
- "opendiscord:channel-category-backup":ODOptionData,
- "opendiscord:channel-categories-claimed":ODOptionData<{user:string,category:string}[]>,
"opendiscord:channel-topic":ODOptionData,
"opendiscord:dm-message-enabled":ODOptionData,
diff --git a/src/core/cli/quickSetup.ts b/src/core/cli/quickSetup.ts
index af19d6d..6313977 100644
--- a/src/core/cli/quickSetup.ts
+++ b/src/core/cli/quickSetup.ts
@@ -31,7 +31,7 @@ interface ODQuickSetupVariables {
globalUserLimit?:number|null,
removeParticipantsOnClose?:boolean,
ticketMessageLayout?:"embed"|"text"|null,
- emojiStyle?:api.ODGeneralJsonConfig_System["emojiStyle"],
+ emojiStyle?:api.ODGeneralJsonConfig_TicketSystem["emojiStyle"],
panelName?:string,
panelDescription?:string,
panelDropdown?:boolean,
@@ -234,7 +234,7 @@ async function renderQuickSetupBotToken(backFn:() => api.ODPromiseVoid){
cli.renderHeader(headerOpts,"โฑ๏ธ Open Ticket Quick Setup: Bot Token")
terminal.bold.blue(stepCount(2)+"Please insert the token of your discord bot.\n")
- terminal.gray("This is used to configure the bot and is then stored securely in the './config/general.json' file.\n\n> ")
+ terminal.gray("This is used to configure the bot and is then stored securely in the './config/general.jsonc' file.\n\n> ")
const answer = await terminal.inputField({
style:terminal.white,
@@ -1203,11 +1203,7 @@ async function saveQuickSetupConfig(){
//GENERAL CONFIG
const generalConfig = opendiscord.configs.get("opendiscord:general")
const generalConfigData: api.ODGeneralJsonConfig_GeneralData = {
- _INFO:{
- support:"https://otdocs.dj-dj.be",
- discord:"https://discord.dj-dj.be",
- version:"open-ticket-"+opendiscord.versions.get("opendiscord:version").toString()
- },
+ _CONFIG_VERSION:"open-ticket-"+opendiscord.versions.get("opendiscord:version").toString(),
token:quickSetupStorage.client?.token ?? "",
tokenFromENV:false,
@@ -1222,8 +1218,29 @@ async function saveQuickSetupConfig(){
textCommands:quickSetupStorage.textCommands ?? false,
status:quickSetupStorage.status ?? {enabled:false,mode:"online",type:"custom",text:"",state:""},
+ logs:{
+ enabled:(typeof quickSetupStorage.logChannel == "string"),
+ channel:quickSetupStorage.logChannel ?? "",
+ logMessages:{
+ creation:{dm:true,logs:true},
+ closing:{dm:true,logs:true},
+ deleting:{dm:true,logs:true},
+ reopening:{dm:false,logs:true},
+ claiming:{dm:false,logs:true},
+ pinning:{dm:false,logs:true},
+ adding:{dm:false,logs:true},
+ removing:{dm:false,logs:true},
+ renaming:{dm:false,logs:true},
+ moving:{dm:true,logs:true},
+ blacklisting:{dm:true,logs:true},
+ transferring:{dm:true,logs:true},
+ topicChange:{dm:false,logs:true},
+ priorityChange:{dm:false,logs:true},
+ reactionRole:{dm:false,logs:true}
+ }
+ },
- system:{
+ ticketSystem:{
preferSlashOverText:quickSetupStorage.slashCommands ?? false,
sendErrorOnUnknownCommand:true,
questionFieldsInCodeBlock:true,
@@ -1234,10 +1251,11 @@ async function saveQuickSetupConfig(){
alwaysShowReason:false,
emojiStyle:quickSetupStorage.emojiStyle ?? "before",
pinEmoji:"๐",
+ closeEmoji:"๐",
- replyOnTicketCreation:false,
+ replyOnTicketCreation:true,
replyOnReactionRole:true,
- askPriorityOnTicketCreation:false,
+ askPriorityOnTicketCreation:true,
removeParticipantsOnClose:quickSetupStorage.removeParticipantsOnClose ?? false,
disableAutocloseAfterReopen:true,
autodeleteRequiresClosedTicket:true,
@@ -1245,7 +1263,7 @@ async function saveQuickSetupConfig(){
allowCloseBeforeMessage:false,
allowCloseBeforeAdminMessage:true,
useTranslatedConfigChecker:true,
- pinFirstTicketMessage:false,
+ pinFirstTicketMessage:true,
enableTicketClaimButtons:true,
enableTicketCloseButtons:true,
@@ -1253,11 +1271,7 @@ async function saveQuickSetupConfig(){
enableTicketDeleteButtons:true,
enableTicketActionWithReason:true,
enableDeleteWithoutTranscript:true,
-
- logs:{
- enabled:(typeof quickSetupStorage.logChannel == "string"),
- channel:quickSetupStorage.logChannel ?? ""
- },
+ enableCreateTicketForOtherUser:true,
limits:{
enabled:(typeof quickSetupStorage.globalUserLimit == "number"),
@@ -1276,49 +1290,41 @@ async function saveQuickSetupConfig(){
showCreator:false,
showParticipants:false
},
-
- permissions:{
- help:"everyone",
- panel:"admin",
- ticket:"everyone",
- close:"admin",
- delete:"admin",
- reopen:"admin",
- claim:"admin",
- unclaim:"admin",
- pin:"admin",
- unpin:"admin",
- move:"admin",
- rename:"admin",
- add:"admin",
- remove:"admin",
- blacklist:"admin",
- stats:"everyone",
- clear:"admin",
- autoclose:"admin",
- autodelete:"admin",
- transfer:"admin",
- topic:"admin",
- priority:"admin",
+
+ closedCategory:{
+ enabled:false,
+ categoryId:""
},
-
- messages:{
- creation:{dm:true,logs:true},
- closing:{dm:true,logs:true},
- deleting:{dm:true,logs:true},
- reopening:{dm:false,logs:true},
- claiming:{dm:false,logs:true},
- pinning:{dm:false,logs:true},
- adding:{dm:false,logs:true},
- removing:{dm:false,logs:true},
- renaming:{dm:false,logs:true},
- moving:{dm:true,logs:true},
- blacklisting:{dm:true,logs:true},
- transferring:{dm:true,logs:true},
- topicChange:{dm:false,logs:true},
- priorityChange:{dm:false,logs:true},
- reactionRole:{dm:false,logs:true}
- }
+ backupCategory:{
+ enabled:false,
+ categoryId:""
+ },
+ claimedCategories:[],
+ },
+ permissions:{
+ help:"everyone",
+ panel:"admin",
+ ticket:"everyone",
+ close:"admin",
+ delete:"admin",
+ reopen:"admin",
+ claim:"admin",
+ unclaim:"admin",
+ pin:"admin",
+ unpin:"admin",
+ move:"admin",
+ rename:"admin",
+ add:"admin",
+ remove:"admin",
+ blacklist:"admin",
+ stats:"everyone",
+ clear:"admin",
+ autoclose:"admin",
+ autodelete:"admin",
+ transfer:"admin",
+ topic:"admin",
+ priority:"admin",
+ transcripts:"admin"
}
}
generalConfig.data = generalConfigData
@@ -1330,10 +1336,11 @@ async function saveQuickSetupConfig(){
{
id:"example-question-1",
name:"Example Question 1",
+ description:"This is a short text input question.",
type:"short",
-
required:true,
- placeholder:"Insert your short answer here!",
+
+ placeholder:"Insert answer...",
length:{
enabled:false,
min:0,
@@ -1343,15 +1350,69 @@ async function saveQuickSetupConfig(){
{
id:"example-question-2",
name:"Example Question 2",
+ description:"This is a paragraph text input question.",
type:"paragraph",
-
required:false,
- placeholder:"Insert your long answer here!",
+
+ placeholder:"Insert answer...",
length:{
enabled:false,
min:0,
max:1000
}
+ },
+ {
+ id:"example-question-3",
+ name:"Example Question 3",
+ description:"This is a dropdown question.",
+ type:"dropdown",
+ required:false,
+
+ placeholder:"Choose your answer...",
+ choices:[
+ {title:"Choice A",description:"Apple",emoji:"๐"},
+ {title:"Choice B",description:"Banana",emoji:"๐"},
+ {title:"Choice C",description:"Orange",emoji:"๐"},
+ {title:"Choice D",description:"Kiwi",emoji:"๐ฅ"}
+ ]
+ },
+ {
+ id:"example-question-4",
+ name:"Example Question 4",
+ description:"This is a radio select question.",
+ type:"radio-select",
+ required:true,
+
+ choices:[
+ {title:"Choice A",description:"Up",selectedByDefault:false},
+ {title:"Choice B",description:"Down",selectedByDefault:false},
+ {title:"Choice C",description:"Left",selectedByDefault:false},
+ {title:"Choice D",description:"Right",selectedByDefault:false}
+ ]
+ },
+ {
+ id:"example-question-5",
+ name:"Example Question 5",
+ description:"This is a checkbox select question.",
+ type:"checkbox-select",
+ required:true,
+
+ limits:{
+ enabled:false,
+ min:0,
+ max:10
+ },
+ choices:[
+ {title:"Choice A",description:"Happiness",selectedByDefault:false},
+ {title:"Choice B",description:"Anger",selectedByDefault:false},
+ {title:"Choice C",description:"Sadness",selectedByDefault:false},
+ {title:"Choice D",description:"Fear",selectedByDefault:false}
+ ]
+ },
+ {
+ id:"example-text-display",
+ type:"text-display",
+ textContents:"This is a text display. It isn't a question, but allows you to display a text, explaination or details."
}
]
questionsConfig.data = questionsConfigData
@@ -1384,9 +1445,6 @@ async function saveQuickSetupConfig(){
prefix:ticket.channelPrefix,
suffix:ticket.channelSuffix,
category:quickSetupStorage.ticketCategory ?? "",
- closedCategory:"",
- backupCategory:"",
- claimedCategory:[],
topic:ticket.description
},
@@ -1482,6 +1540,7 @@ async function saveQuickSetupConfig(){
},
settings:{
dropdownPlaceholder:"Open a ticket",
+ maximumButtonsPerRow:5,
enableMaxTicketsWarningInText:(quickSetupStorage.panelLayout == "text" && (quickSetupStorage.panelMaxTicketsWarning ?? false)),
enableMaxTicketsWarningInEmbed:(quickSetupStorage.panelLayout == "embed" && (quickSetupStorage.panelMaxTicketsWarning ?? false)),
diff --git a/src/core/main.ts b/src/core/main.ts
index fc5dceb..9ba8b41 100644
--- a/src/core/main.ts
+++ b/src/core/main.ts
@@ -58,7 +58,7 @@ export class ODOpenTicketMain extends api.ODMain {
priorities: api.ODMappedPriorityManager
constructor(){
- const version = api.ODVersion.fromString("opendiscord:version","v4.1.3")
+ const version = api.ODVersion.fromString("opendiscord:version","v4.2.0")
const debugfile = new api.ODDebugFileManager("./","otdebug.txt",5000,version)
const console = new api.ODConsoleManager(100,debugfile)
const debug = new api.ODDebugger(console)
@@ -108,7 +108,7 @@ export class ODOpenTicketMain extends api.ODMain {
},"openticket")
this.livestatus.useMain(this)
- this.versions.add(api.ODVersion.fromString("opendiscord:version","v4.1.3"))
+ this.versions.add(api.ODVersion.fromString("opendiscord:version","v4.2.0"))
this.versions.add(api.ODVersion.fromString("opendiscord:transcripts","v2.1.0"))
//OPEN TICKET
diff --git a/src/core/mappings/config.ts b/src/core/mappings/config.ts
index 8e6c726..4f2114b 100644
--- a/src/core/mappings/config.ts
+++ b/src/core/mappings/config.ts
@@ -10,20 +10,20 @@ import { ODRoleUpdateMode } from "../api/role.js"
* It's used to generate typescript declarations for this class.
*/
export interface ODConfigManagerIdMappings extends api.ODConfigManagerIdConstraint {
- "opendiscord:general":ODGeneralJsonConfig,
- "opendiscord:questions":ODQuestionsJsonConfig,
- "opendiscord:options":ODOptionsJsonConfig,
- "opendiscord:panels":ODPanelsJsonConfig,
- "opendiscord:transcripts":ODTranscriptsJsonConfig
+ "opendiscord:general":ODGeneralJsonCommentsConfig,
+ "opendiscord:questions":ODQuestionsJsonCommentsConfig,
+ "opendiscord:options":ODOptionsJsonCommentsConfig,
+ "opendiscord:panels":ODPanelsJsonCommentsConfig,
+ "opendiscord:transcripts":ODTranscriptsJsonCommentsConfig
}
///////////////////////////////////////
// CONFIG STRUCTURES, VALUES & TYPES
-// --> general.json
+// --> general.jsonc
///////////////////////////////////////
/**## ODGeneralJsonConfig_Status `interface`
- * This interface is an object which has all properties for the status object in the `general.json` config!
+ * This interface is an object which has all properties for the status object in the `general.jsonc` config!
*/
export interface ODGeneralJsonConfig_Status {
/**Is the status enabled? */
@@ -39,7 +39,7 @@ export interface ODGeneralJsonConfig_Status {
}
/**## ODGeneralJsonConfig_MessageSettings `interface`
- * This interface is an object which has all properties for the "system"."messages".... object in the `general.json` config!
+ * This interface is an object which has all properties for the "system"."messages".... object in the `general.jsonc` config!
*/
export interface ODGeneralJsonConfig_MessageSettings {
/**Enable sending DM logs to the ticket creator for this action. */
@@ -49,22 +49,10 @@ export interface ODGeneralJsonConfig_MessageSettings {
}
/**## ODGeneralJsonConfig_CmdPermissionSettingsType `type`
- * This type is a collection of command permission settings for the "system"."permissions".... object in the `general.json` config!
+ * This type is a collection of command permission settings for the "system"."permissions".... object in the `general.jsonc` config!
*/
export type ODGeneralJsonConfig_CmdPermissionSettingsType = "admin"|"everyone"|"none"|string
-/**## ODGeneralJsonConfig_Info `interface`
- * This object contains a few URLs and metadata for the config.
- */
-export interface ODGeneralJsonConfig_Info {
- /**A link to the Open Ticket documentation. */
- support:string,
- /**A link to the DJdj Development discord server. */
- discord:string,
- /**The version of Open Ticket this config is compatible with. */
- version:string
-}
-
/**## ODGeneralJsonConfig_SystemLogs `interface`
* All settings related to the log channel.
*/
@@ -72,7 +60,9 @@ export interface ODGeneralJsonConfig_SystemLogs {
/**Enable logging. Individual actions should still be added via the `"system"."messages"..."logs"` */
enabled:boolean,
/**The channel to send logs to. */
- channel:string
+ channel:string,
+ /**Configure dm & log messages for all Open Ticket commands & actions. */
+ logMessages:ODGeneralJsonConfig_LogMessages
}
/**## ODGeneralJsonConfig_SystemLimits `interface`
@@ -137,12 +127,13 @@ export interface ODGeneralJsonConfig_SystemPermissions {
transfer:ODGeneralJsonConfig_CmdPermissionSettingsType,
topic:ODGeneralJsonConfig_CmdPermissionSettingsType,
priority:ODGeneralJsonConfig_CmdPermissionSettingsType,
+ transcripts:ODGeneralJsonConfig_CmdPermissionSettingsType,
}
-/**## ODGeneralJsonConfig_SystemMessages `interface`
+/**## ODGeneralJsonConfig_LogMessages `interface`
* Configure dm & log messages for all Open Ticket commands & actions.
*/
-export interface ODGeneralJsonConfig_SystemMessages {
+export interface ODGeneralJsonConfig_LogMessages {
creation:ODGeneralJsonConfig_MessageSettings,
closing:ODGeneralJsonConfig_MessageSettings,
deleting:ODGeneralJsonConfig_MessageSettings,
@@ -160,10 +151,10 @@ export interface ODGeneralJsonConfig_SystemMessages {
reactionRole:ODGeneralJsonConfig_MessageSettings,
}
-/**## ODGeneralJsonConfig_System `interface`
+/**## ODGeneralJsonConfig_TicketSystem `interface`
* All settings related to the ticket system.
*/
-export interface ODGeneralJsonConfig_System {
+export interface ODGeneralJsonConfig_TicketSystem {
/**Prefer slash-commands over text-commands when displaying them in menu's and messages. */
preferSlashOverText:boolean,
/**Reply with "unknown command" when the prefix is used without a valid command. */
@@ -184,6 +175,8 @@ export interface ODGeneralJsonConfig_System {
emojiStyle:"before"|"after"|"double"|"disabled",
/**The emoji used when pinning tickets. This is '๐' by default. */
pinEmoji:string,
+ /**The emoji used when closing tickets. This is '๐' by default. */
+ closeEmoji:string,
/**Reply with an ephemeral message when a ticket is created. */
replyOnTicketCreation:boolean,
@@ -220,9 +213,8 @@ export interface ODGeneralJsonConfig_System {
enableTicketActionWithReason:boolean,
/**Enable/disable the delete without transcript feature (button & /delete command). */
enableDeleteWithoutTranscript:boolean,
-
- /**All settings related to the log channel. */
- logs:ODGeneralJsonConfig_SystemLogs,
+ /**Enable/disable creating tickets for other users with /ticket . (ADMIN ONLY) */
+ enableCreateTicketForOtherUser:boolean,
/**All settings related to global ticket limits. */
limits:ODGeneralJsonConfig_SystemLimits,
@@ -230,19 +222,31 @@ export interface ODGeneralJsonConfig_System {
/**All global channel topic settings. */
channelTopic:ODGeneralJsonConfig_SystemChannelTopic,
- /**Configure permissions for all Open Ticket commands & actions. */
- permissions:ODGeneralJsonConfig_SystemPermissions,
-
- /**Configure dm & log messages for all Open Ticket commands & actions. */
- messages:ODGeneralJsonConfig_SystemMessages
+ /**Move closed tickets to this channel category. */
+ closedCategory:{
+ enabled:boolean
+ categoryId:string
+ },
+ /**Create tickets in this channel category when the original category is full (max 50 channels). */
+ backupCategory:{
+ enabled:boolean
+ categoryId:string
+ },
+ /**Move claimed tickets to the matching channel category of the user that claimed the ticket. */
+ claimedCategories:{
+ /**The user who claimed the ticket. */
+ user:string,
+ /**The category to move the ticket to. */
+ category:string
+ }[],
}
/**## ODGeneralJsonConfig_GeneralData `interface`
- * All contents of the `general.json` config file.
+ * All contents of the `general.jsonc` config file.
*/
export interface ODGeneralJsonConfig_GeneralData {
/**This object contains a few URLs and metadata for the config. */
- _INFO:ODGeneralJsonConfig_Info,
+ _CONFIG_VERSION:string,
/**The token of the bot. (Empty when using `tokenFromENV`) */
token:string,
@@ -267,18 +271,24 @@ export interface ODGeneralJsonConfig_GeneralData {
/**All settings related to the status of the bot. */
status:ODGeneralJsonConfig_Status,
-
+
/**All settings related to the ticket system. */
- system:ODGeneralJsonConfig_System
+ ticketSystem:ODGeneralJsonConfig_TicketSystem,
+
+ /**Configure permissions for all Open Ticket commands & actions. */
+ permissions:ODGeneralJsonConfig_SystemPermissions,
+
+ /**All settings related to the log channel. */
+ logs:ODGeneralJsonConfig_SystemLogs,
}
///////////////////////////////////////
// CONFIG STRUCTURES, VALUES & TYPES
-// --> options.json
+// --> options.jsonc
///////////////////////////////////////
/**## ODOptionsJsonConfig_BaseOption `interface`
- * This interface is an object which has all basic properties for options in the `options.json` config!
+ * The basic properties for options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_BaseOption {
/**The id of this option. */
@@ -288,7 +298,7 @@ export interface ODOptionsJsonConfig_BaseOption {
/**The description of this option. */
description:string,
/**The type of this option. This type also determines the other option-specific variables. */
- type:"ticket"|"website"|"role",
+ type:"ticket"|"website"|"role"|"sub-panel",
/**All settings related to the button for the 3 option types. */
button:{
/**The emoji of the button. (can also be empty) */
@@ -299,7 +309,7 @@ export interface ODOptionsJsonConfig_BaseOption {
}
/**## ODOptionsJsonConfig_OptionButtonSettings `interface`
- * This interface is an object which has all button settings for ticket & reaction role options in the `options.json` config!
+ * The button settings for ticket, sub-panel & reaction role options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_OptionButtonSettings {
/**The emoji of the button. (can also be empty) */
@@ -311,7 +321,7 @@ export interface ODOptionsJsonConfig_OptionButtonSettings {
}
/**## ODOptionsJsonConfig_TicketOptionEmbedSettings `interface`
- * This interface is an object which has all message embed settings for ticket options in the `options.json` config!
+ * The message embed settings for ticket options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_TicketOptionEmbedSettings {
/**Is this embed enabled? */
@@ -340,7 +350,7 @@ export interface ODOptionsJsonConfig_TicketOptionEmbedSettings {
}
/**## ODOptionsJsonConfig_TicketOptionPingSettings `interface`
- * This interface is an object which has all message ping settings for ticket options in the `options.json` config!
+ * The message ping settings for ticket options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_TicketOptionPingSettings {
/**Ping `@here`. */
@@ -361,23 +371,12 @@ export interface ODOptionsJsonConfig_TicketOptionChannelSettings {
suffix:"user-name"|"user-nickname"|"user-id"|"random-number"|"random-hex"|"counter-dynamic"|"counter-fixed",
/**An optional discord category id to create this ticket in. */
category:string,
- /**An optional discord category id to move this ticket to when closed. */
- closedCategory:string,
- /**An optional discord category id to create this ticket in when the primary one is full (max. 50 tickets). */
- backupCategory:string,
- /**A list of discord category ids to move this ticket to when claimed by a specific user. */
- claimedCategory:{
- /**The user which claimed the ticket. */
- user:string,
- /**The category to move the ticket to when claimed by this user. */
- category:string
- }[],
/**The channel topic shown at the top of the channel in discord. */
topic:string
}
/**## ODOptionsJsonConfig_TicketOption `interface`
- * This interface is an object which has all ticket properties for options in the `options.json` config!
+ * All properties for ticket options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_TicketOption extends ODOptionsJsonConfig_BaseOption {
type:"ticket",
@@ -388,7 +387,7 @@ export interface ODOptionsJsonConfig_TicketOption extends ODOptionsJsonConfig_Ba
readonlyAdmins:string[],
/**When enabled, blacklisted users can still create this ticket type. (used for appeals, etc) */
allowCreationByBlacklistedUsers:boolean,
- /**A list of valid question ids from the `questions.json` config. */
+ /**A list of valid question ids from the `questions.jsonc` config. */
questions:string[],
/**All settings related to the ticket channel itself. */
channel:ODOptionsJsonConfig_TicketOptionChannelSettings,
@@ -460,7 +459,7 @@ export interface ODOptionsJsonConfig_TicketOption extends ODOptionsJsonConfig_Ba
}
/**## ODOptionsJsonConfig_WebsiteOption `interface`
- * This interface is an object which has all website properties for options in the `options.json` config!
+ * All properties for website options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_WebsiteOption extends ODOptionsJsonConfig_BaseOption {
type:"website",
@@ -469,7 +468,7 @@ export interface ODOptionsJsonConfig_WebsiteOption extends ODOptionsJsonConfig_B
}
/**## ODOptionsJsonConfig_RoleOption `interface`
- * This interface is an object which has all reaction role properties for options in the `options.json` config!
+ * All properties for reaction-role options in the `options.jsonc` config!
*/
export interface ODOptionsJsonConfig_RoleOption extends ODOptionsJsonConfig_BaseOption {
type:"role",
@@ -484,18 +483,28 @@ export interface ODOptionsJsonConfig_RoleOption extends ODOptionsJsonConfig_Base
addOnMemberJoin:boolean
}
-/**## ODOptionsJsonConfig_OptionsData `type`
- * All contents of the `options.json` config file.
+/**## ODOptionsJsonConfig_SubPanelOption `interface`
+ * All properties for sub-panel options in the `options.jsonc` config!
*/
-export type ODOptionsJsonConfig_OptionsData = (ODOptionsJsonConfig_TicketOption|ODOptionsJsonConfig_WebsiteOption|ODOptionsJsonConfig_RoleOption)[]
+export interface ODOptionsJsonConfig_SubPanelOption extends ODOptionsJsonConfig_BaseOption {
+ type:"sub-panel",
+ button:ODOptionsJsonConfig_OptionButtonSettings,
+ /**The panel ID of the sub-panel to show when the button is clicked. */
+ subPanelId:string
+}
+
+/**## ODOptionsJsonConfig_OptionsData `type`
+ * All contents of the `options.jsonc` config file.
+ */
+export type ODOptionsJsonConfig_OptionsData = (ODOptionsJsonConfig_TicketOption|ODOptionsJsonConfig_WebsiteOption|ODOptionsJsonConfig_RoleOption|ODOptionsJsonConfig_SubPanelOption)[]
///////////////////////////////////////
// CONFIG STRUCTURES, VALUES & TYPES
-// --> panels.json
+// --> panels.jsonc
///////////////////////////////////////
/**## ODPanelsJsonConfig_PanelEmbedSettings `interface`
- * This interface is an object which has all message embed settings for panels in the `panels.json` config!
+ * This interface is an object which has all message embed settings for panels in the `panels.jsonc` config!
*/
export interface ODPanelsJsonConfig_PanelEmbedSettings {
/**Is this embed enabled? */
@@ -535,6 +544,8 @@ export interface ODPanelsJsonConfig_PanelEmbedSettings {
export interface ODPanelsJsonConfig_PanelSettings {
/**The placeholder used in the dropdown when enabled. */
dropdownPlaceholder:string,
+ /**The maximum amount of option buttons before starting a new row. */
+ maximumButtonsPerRow:number
/**Enable a max tickets warning in the text contents. */
enableMaxTicketsWarningInText:boolean,
@@ -554,7 +565,7 @@ export interface ODPanelsJsonConfig_PanelSettings {
}
/**## ODPanelsJsonConfig_Panel `interface`
- * This interface is an object which has all properties for panels in the `panels.json` config!
+ * This interface is an object which has all properties for panels in the `panels.jsonc` config!
*/
export interface ODPanelsJsonConfig_Panel {
/**The id of this panel. */
@@ -563,7 +574,7 @@ export interface ODPanelsJsonConfig_Panel {
name:string,
/**When enabled, the panel uses a dropdown instead of buttons. */
dropdown:boolean,
- /**A list of valid options ids from the `options.json` config. */
+ /**A list of valid options ids from the `options.jsonc` config. */
options:string[],
/**The raw text contents of this panel. (empty for embed only) */
@@ -575,19 +586,19 @@ export interface ODPanelsJsonConfig_Panel {
}
/**## ODPanelsJsonConfig_PanelsData `type`
- * All contents of the `panels.json` config file.
+ * All contents of the `panels.jsonc` config file.
*/
export type ODPanelsJsonConfig_PanelsData = ODPanelsJsonConfig_Panel[]
///////////////////////////////////////
// CONFIG STRUCTURES, VALUES & TYPES
-// --> questions.json
+// --> questions.jsonc
///////////////////////////////////////
-/**## ODQuestionsJsonConfig_QuestionLengthSettings `interface`
+/**## ODQuestionsJsonConfig_TextLengthLimits `interface`
* This interface is a collection of settings related to length validation in a question.
*/
-export interface ODQuestionsJsonConfig_QuestionLengthSettings {
+export interface ODQuestionsJsonConfig_TextLengthLimits {
/**Enable text length verification. */
enabled:boolean,
/**The minimum text input length. */
@@ -596,48 +607,138 @@ export interface ODQuestionsJsonConfig_QuestionLengthSettings {
max:number
}
+/**## ODQuestionsJsonConfig_CheckboxLimits `interface`
+ * The required amount of checkboxes validation in a question.
+ */
+export interface ODQuestionsJsonConfig_CheckboxLimits {
+ /**Enable checkbox limits. */
+ enabled:boolean,
+ /**The minimum amount of selected checkboxes. */
+ min:number,
+ /**The maximum amount of selected checkboxes. */
+ max:number
+}
+
+/**## ODQuestionsJsonConfig_DropdownChoice `interface`
+ * A dropdown choice used in `ODQuestionsJsonConfig_DropdownQuestion`
+ */
+export interface ODQuestionsJsonConfig_DropdownChoice {
+ /**The title of the choice. */
+ title:string,
+ /**The optional description of the choice. (Leave empty for none) */
+ description:string,
+ /**The optional emoji of the choice. (Leave empty for none) */
+ emoji:string
+}
+
+/**## ODQuestionsJsonConfig_RadioCheckboxChoice `interface`
+ * A radio/checkbox choice used in `ODQuestionsJsonConfig_RadioSelectQuestion` & `ODQuestionsJsonConfig_CheckboxSelectQuestion`
+ */
+export interface ODQuestionsJsonConfig_RadioCheckboxChoice {
+ /**The title of the choice. */
+ title:string,
+ /**The optional description of the choice. (Leave empty for none) */
+ description:string,
+ /**Is this choice selected by default? */
+ selectedByDefault:boolean
+}
+
/**## ODQuestionsJsonConfig_BaseQuestion `interface`
- * This interface is an object which has all universal properties for questions in the `questions.json` config!
+ * This interface is an object which has all universal properties for questions in the `questions.jsonc` config!
*/
export interface ODQuestionsJsonConfig_BaseQuestion {
/**The id of this question. */
id:string,
/**The name of this question. */
name:string,
+ /**The description of this question. (Leave empty for none) */
+ description:string,
/**The type of this question. */
- type:"short"|"paragraph",
+ type:"short"|"paragraph"|"text-display"|"dropdown"|"radio-select"|"checkbox-select",
/**Is this question required? */
required:boolean,
}
+/**## ODQuestionsJsonConfig_TextDisplayQuestion `interface`
+ * All properties for a text-display in the `questions.jsonc` config!
+ */
+export interface ODQuestionsJsonConfig_TextDisplayQuestion {
+ /**The id of this text-display. */
+ id:string,
+ /**The type of this question. */
+ type:"text-display",
+ /**The text contents to show in the modal. */
+ textContents:string
+}
+
/**## ODQuestionsJsonConfig_ShortQuestion `interface`
- * This interface is an object which has all properties for short questions in the `questions.json` config!
+ * All properties for short questions in the `questions.jsonc` config!
*/
export interface ODQuestionsJsonConfig_ShortQuestion extends ODQuestionsJsonConfig_BaseQuestion {
+ type:"short",
/**A placeholder for the question. */
placeholder:string,
/**A collection of settings related to length validation in a question. */
- length:ODQuestionsJsonConfig_QuestionLengthSettings
+ length:ODQuestionsJsonConfig_TextLengthLimits
}
/**## ODQuestionsJsonConfig_ParagraphQuestion `interface`
- * This interface is an object which has all properties for paragraph questions in the `questions.json` config!
+ * All properties for paragraph questions in the `questions.jsonc` config!
*/
export interface ODQuestionsJsonConfig_ParagraphQuestion extends ODQuestionsJsonConfig_BaseQuestion {
+ type:"paragraph",
/**A placeholder for the question. */
placeholder:string,
/**A collection of settings related to length validation in a question. */
- length:ODQuestionsJsonConfig_QuestionLengthSettings
+ length:ODQuestionsJsonConfig_TextLengthLimits
+}
+
+/**## ODQuestionsJsonConfig_DropdownQuestion `interface`
+ * All properties for dropdown questions in the `questions.jsonc` config!
+ */
+export interface ODQuestionsJsonConfig_DropdownQuestion extends ODQuestionsJsonConfig_BaseQuestion {
+ type:"dropdown",
+ /**A placeholder for the dropdown. */
+ placeholder:string,
+ /**A list of maximum 25 dropdown choices. */
+ choices:ODQuestionsJsonConfig_DropdownChoice[]
+}
+
+/**## ODQuestionsJsonConfig_RadioSelectQuestion `interface`
+ * All properties for radio select questions in the `questions.jsonc` config!
+ */
+export interface ODQuestionsJsonConfig_RadioSelectQuestion extends ODQuestionsJsonConfig_BaseQuestion {
+ type:"radio-select",
+ /**A list of minimum 2, maximum 10 radio choices. */
+ choices:ODQuestionsJsonConfig_RadioCheckboxChoice[]
+}
+
+/**## ODQuestionsJsonConfig_CheckboxSelectQuestion `interface`
+ * All properties for checkbox select questions in the `questions.jsonc` config!
+ */
+export interface ODQuestionsJsonConfig_CheckboxSelectQuestion extends ODQuestionsJsonConfig_BaseQuestion {
+ type:"checkbox-select",
+ /**Verify the checked amount of checkboxes with a minimum & maximum. */
+ limits:ODQuestionsJsonConfig_CheckboxLimits
+ /**A list of minimum 1, maximum 10 checkbox choices. */
+ choices:ODQuestionsJsonConfig_RadioCheckboxChoice[]
}
/**## ODQuestionsJsonConfig_QuestionsData `type`
- * All contents of the `questions.json` config file.
+ * All contents of the `questions.jsonc` config file.
*/
-export type ODQuestionsJsonConfig_QuestionsData = (ODQuestionsJsonConfig_ShortQuestion|ODQuestionsJsonConfig_ParagraphQuestion)[]
+export type ODQuestionsJsonConfig_QuestionsData = (
+ ODQuestionsJsonConfig_ShortQuestion|
+ ODQuestionsJsonConfig_ParagraphQuestion|
+ ODQuestionsJsonConfig_TextDisplayQuestion|
+ ODQuestionsJsonConfig_DropdownQuestion|
+ ODQuestionsJsonConfig_RadioSelectQuestion|
+ ODQuestionsJsonConfig_CheckboxSelectQuestion
+)[]
///////////////////////////////////////
// CONFIG STRUCTURES, VALUES & TYPES
-// --> transcripts.json
+// --> transcripts.jsonc
///////////////////////////////////////
/**## ODTranscriptsJsonConfig_TranscriptsTextLayout `interface`
@@ -712,7 +813,7 @@ export interface ODTranscriptsJsonConfig_TranscriptsHtmlLayout {
}
/**## ODTranscriptsJsonConfig_TranscriptsData `interface`
- * All contents of the `transcripts.json` config file.
+ * All contents of the `transcripts.jsonc` config file.
*/
export interface ODTranscriptsJsonConfig_TranscriptsData {
/**All general settings related to transcripts. */
@@ -760,27 +861,27 @@ export interface ODTranscriptsJsonConfig_TranscriptsData {
*/
export class ODMappedConfigManager extends api.ODConfigManager {}
-/**## ODGeneralJsonConfig `class
- * A special class with types for the Open Ticket `config/general.json` config file
+/**## ODGeneralJsonCommentsConfig `class
+ * A special class with types for the Open Ticket `config/general.jsonc` config file
*/
-export class ODGeneralJsonConfig extends api.ODJsonConfig {}
+export class ODGeneralJsonCommentsConfig extends api.ODJsonCommentsConfig {}
-/**## ODQuestionsJsonConfig `class
- * A special class with types for the Open Ticket `config/questions.json` config file
+/**## ODQuestionsJsonCommentsConfig `class
+ * A special class with types for the Open Ticket `config/questions.jsonc` config file
*/
-export class ODQuestionsJsonConfig extends api.ODJsonConfig {}
+export class ODQuestionsJsonCommentsConfig extends api.ODJsonCommentsConfig {}
-/**## ODOptionsJsonConfig `class
- * A special class with types for the Open Ticket `config/options.json` config file
+/**## ODOptionsJsonCommentsConfig `class
+ * A special class with types for the Open Ticket `config/options.jsonc` config file
*/
-export class ODOptionsJsonConfig extends api.ODJsonConfig {}
+export class ODOptionsJsonCommentsConfig extends api.ODJsonCommentsConfig {}
-/**## ODPanelsJsonConfig `class
- * A special class with types for the Open Ticket `config/panels.json` config file
+/**## ODPanelsJsonCommentsConfig `class
+ * A special class with types for the Open Ticket `config/panels.jsonc` config file
*/
-export class ODPanelsJsonConfig extends api.ODJsonConfig {}
+export class ODPanelsJsonCommentsConfig extends api.ODJsonCommentsConfig {}
-/**## ODTranscriptsJsonConfig `class
- * A special class with types for the Open Ticket `config/transcripts.json` config file
+/**## ODTranscriptsJsonCommentsConfig `class
+ * A special class with types for the Open Ticket `config/transcripts.jsonc` config file
*/
-export class ODTranscriptsJsonConfig extends api.ODJsonConfig {}
\ No newline at end of file
+export class ODTranscriptsJsonCommentsConfig extends api.ODJsonCommentsConfig {}
\ No newline at end of file
diff --git a/src/core/mappings/fuse.ts b/src/core/mappings/fuse.ts
index 1b9255a..6e1ae00 100644
--- a/src/core/mappings/fuse.ts
+++ b/src/core/mappings/fuse.ts
@@ -6,21 +6,21 @@ import * as api from "@open-discord-bots/framework/api"
export interface ODOpenTicketFuseList {
/**Load the default Open Ticket ticket priority levels. */
priorityLoading:boolean,
- /**Load the default Open Ticket questions (from `config/questions.json`) */
+ /**Load the default Open Ticket questions (from `config/questions.jsonc`) */
questionLoading:boolean,
- /**Load the default Open Ticket options (from `config/options.json`) */
+ /**Load the default Open Ticket options (from `config/options.jsonc`) */
optionLoading:boolean,
- /**Load the default Open Ticket panels (from `config/panels.json`) */
+ /**Load the default Open Ticket panels (from `config/panels.jsonc`) */
panelLoading:boolean,
/**Load the default Open Ticket tickets (from `database/tickets.json`) */
ticketLoading:boolean,
- /**Load the default Open Ticket reaction roles (from `config/options.json`) */
+ /**Load the default Open Ticket reaction roles (from `config/options.jsonc`) */
roleLoading:boolean,
/**Load the default Open Ticket blacklist (from `database/users.json`) */
blacklistLoading:boolean,
/**Load the default Open Ticket transcript compilers. */
transcriptCompilerLoading:boolean,
- /**Load the default Open Ticket transcript history (from `database/transcripts.json`) */
+ /**Load the default Open Ticket transcript history (from `database/transcripts.jsonc`) */
transcriptHistoryLoading:boolean,
/**The interval in milliseconds that are between autoclose timeout checkers. */
autocloseCheckInterval:number,
diff --git a/src/core/startup/manageMigration.ts b/src/core/startup/manageMigration.ts
index ad4898c..5bd5eec 100644
--- a/src/core/startup/manageMigration.ts
+++ b/src/core/startup/manageMigration.ts
@@ -1,9 +1,24 @@
import {opendiscord, api, utilities} from "../../index.js"
import fs from "fs"
+import path from "path"
+
+/**Check if the no-migration flag is active. */
+function isMigrationAllowedFromFlag(){
+ return (!process.argv.includes("--no-migration") && !process.argv.includes("-nm"))
+}
+
+/**Read the global.json database raw to detect the last version of the bot. */
+function getRawLastVersion(){
+ const isDevDatabase = process.argv.includes("--dev-database") || process.argv.includes("-dd")
+ const globalDatabaseLocation = path.join(process.cwd(),(isDevDatabase) ? "./devdatabase/global.json" : "./database/global.json")
+ const rawData: api.ODJsonDatabaseStructure = JSON.parse(fs.readFileSync(globalDatabaseLocation).toString())
+ const lastVersion = rawData.find((d) => d.category == "opendiscord:last-version" && d.key == "opendiscord:version")?.value ?? null
+ return lastVersion as string|null
+}
/**Check if migration is required. Returns the last version used in the database. */
async function isMigrationRequired(): Promise {
- const rawVersion = await opendiscord.databases.get("opendiscord:global").get("opendiscord:last-version","opendiscord:version")
+ const rawVersion = getRawLastVersion()
if (!rawVersion) return false
const version = api.ODVersion.fromString("opendiscord:last-version",rawVersion)
if (opendiscord.versions.get("opendiscord:version").compare(version) == "higher"){
@@ -20,47 +35,6 @@ async function saveAllVersionsToDatabase(){
})
}
-export async function loadVersionMigrationSystem(){
- //ENTER MIGRATION CONTEXT
- await preloadMigrationContext()
-
- const lastVersion = await isMigrationRequired()
-
- //save last version to database (OR set to current version if no migration is required)
- opendiscord.versions.add(lastVersion ? lastVersion : api.ODVersion.fromString("opendiscord:last-version",opendiscord.versions.get("opendiscord:version").toString()))
-
- if (lastVersion && !opendiscord.flags.get("opendiscord:no-migration").value){
- //MIGRATION IS REQUIRED
- opendiscord.log("Detected old data!","info")
- opendiscord.log("Starting closed API context...","debug")
- await utilities.timer(600)
- opendiscord.log("Migrating data to new version...","debug")
- await loadAllVersionMigrations(lastVersion)
- opendiscord.log("Stopping closed API context...","debug")
- await utilities.timer(400)
- opendiscord.log("All data is now up to date!","info")
- await utilities.timer(200)
- console.log("---------------------------------------------------------------------")
- }
- saveAllVersionsToDatabase()
-
- //DEFAULT FLAGS
- if (opendiscord.flags.exists("opendiscord:no-plugins") && opendiscord.flags.get("opendiscord:no-plugins").value) opendiscord.sharedFuses.setFuse("pluginLoading",false)
- if (opendiscord.flags.exists("opendiscord:soft-plugins") && opendiscord.flags.get("opendiscord:soft-plugins").value) opendiscord.sharedFuses.setFuse("softPluginLoading",true)
- if (opendiscord.flags.exists("opendiscord:crash") && opendiscord.flags.get("opendiscord:crash").value) opendiscord.sharedFuses.setFuse("crashOnError",true)
- if (opendiscord.flags.exists("opendiscord:force-slash-update") && opendiscord.flags.get("opendiscord:force-slash-update").value){
- opendiscord.sharedFuses.setFuse("forceSlashCommandRegistration",true)
- opendiscord.sharedFuses.setFuse("forceContextMenuRegistration",true)
- }
- if (opendiscord.flags.exists("opendiscord:silent") && opendiscord.flags.get("opendiscord:silent").value) opendiscord.console.silent = true
-
-
- //LEAVE MIGRATION CONTEXT
- await unloadMigrationContext()
-
- return lastVersion
-}
-
/**Initialize the migration context by loading the built-in flags, configs & databases. */
async function preloadMigrationContext(){
opendiscord.debug.debug("-- MIGRATION CONTEXT START --")
@@ -73,6 +47,52 @@ async function preloadMigrationContext(){
opendiscord.debug.visible = true
}
+export async function loadVersionMigrationSystem(){
+ const lastVersion = await isMigrationRequired()
+
+ //save last version in version manager (OR set to current version if no migration is required)
+ opendiscord.versions.add(lastVersion ? lastVersion : api.ODVersion.fromString("opendiscord:last-version",opendiscord.versions.get("opendiscord:version").toString()))
+
+ //MIGRATION IS REQUIRED
+ if (lastVersion && isMigrationAllowedFromFlag()){
+ //BEFORE STARTUP MIGRATION
+ opendiscord.log("Detected old data!","info")
+ await loadBeforeStartupMigrations(lastVersion)
+ }
+
+ //ENTER MIGRATION CONTEXT (must be separate for flags to work)
+ await preloadMigrationContext()
+
+ if (lastVersion && isMigrationAllowedFromFlag()){
+ //CONTEXT MIGRATION
+ opendiscord.log("Starting restricted API context...","debug")
+ await utilities.timer(600)
+ opendiscord.log("Migrating data to new version...","debug")
+ await loadContextMigrations(lastVersion)
+ opendiscord.log("Stopping restricted API context...","debug")
+ await utilities.timer(400)
+ opendiscord.log("All data is now up to date!","info")
+ await utilities.timer(200)
+ console.log("---------------------------------------------------------------------")
+ }
+ saveAllVersionsToDatabase()
+
+ //SET FUSES & PROPERTIES OF SPECIAL FLAGS
+ if (opendiscord.flags.exists("opendiscord:no-plugins") && opendiscord.flags.get("opendiscord:no-plugins").value) opendiscord.sharedFuses.setFuse("pluginLoading",false)
+ if (opendiscord.flags.exists("opendiscord:soft-plugins") && opendiscord.flags.get("opendiscord:soft-plugins").value) opendiscord.sharedFuses.setFuse("softPluginLoading",true)
+ if (opendiscord.flags.exists("opendiscord:crash") && opendiscord.flags.get("opendiscord:crash").value) opendiscord.sharedFuses.setFuse("crashOnError",true)
+ if (opendiscord.flags.exists("opendiscord:force-slash-update") && opendiscord.flags.get("opendiscord:force-slash-update").value){
+ opendiscord.sharedFuses.setFuse("forceSlashCommandRegistration",true)
+ opendiscord.sharedFuses.setFuse("forceContextMenuRegistration",true)
+ }
+ if (opendiscord.flags.exists("opendiscord:silent") && opendiscord.flags.get("opendiscord:silent").value) opendiscord.console.silent = true
+
+ //LEAVE MIGRATION CONTEXT
+ await unloadMigrationContext()
+
+ return lastVersion
+}
+
/**Unload the migration context to start the bot normally. */
async function unloadMigrationContext(){
opendiscord.debug.visible = false
@@ -98,8 +118,8 @@ function createMigrationBackup(){
else fs.cpSync("./database/","./.backup/database/",{force:true,recursive:true})
}
-/**Execute all version migration functions which are handled in the restricted migration context. */
-async function loadAllVersionMigrations(lastVersion:api.ODVersion){
+/**Execute all version migration functions which are handled before any flags, configs or databases are loaded. */
+async function loadBeforeStartupMigrations(lastVersion:api.ODVersion){
const migrations = (await import("./migration.js")).migrations
migrations.sort((a,b) => {
const comparison = a.version.compare(b.version)
@@ -114,10 +134,36 @@ async function loadAllVersionMigrations(lastVersion:api.ODVersion){
for (const migration of migrations){
if (migration.version.compare(lastVersion) == "higher"){
- const success = await migration.migrate()
+ const success = await migration.migrateBeforeStartup()
if (success) opendiscord.log("Migrated data to "+migration.version.toString()+"!","debug",[
{key:"success",value:success ? "true" : "false"},
- {key:"afterInit",value:"false"}
+ {key:"type",value:"before-startup"}
+ ])
+ else throw new api.ODSystemError("Migration Error: Unable to migrate database & config to the new version of the bot.")
+ }
+ }
+}
+
+/**Execute all version migration functions which are handled in the restricted migration context. */
+async function loadContextMigrations(lastVersion:api.ODVersion){
+ const migrations = (await import("./migration.js")).migrations
+ migrations.sort((a,b) => {
+ const comparison = a.version.compare(b.version)
+ if (comparison == "equal") return 0
+ else if (comparison == "higher") return 1
+ else return -1
+ })
+ if (migrations.length > 0){
+ //create backup of config & database
+ createMigrationBackup()
+ }
+
+ for (const migration of migrations){
+ if (migration.version.compare(lastVersion) == "higher"){
+ const success = await migration.migrateInContext()
+ if (success) opendiscord.log("Migrated data to "+migration.version.toString()+"!","debug",[
+ {key:"success",value:success ? "true" : "false"},
+ {key:"type",value:"restricted-context"}
])
else throw new api.ODSystemError("Migration Error: Unable to migrate database & config to the new version of the bot.")
}
@@ -125,7 +171,7 @@ async function loadAllVersionMigrations(lastVersion:api.ODVersion){
}
/**Execute all version migration functions which are handled in the normal startup sequence. */
-export async function loadAllAfterInitVersionMigrations(lastVersion:api.ODVersion){
+export async function loadAfterStartupMigrations(lastVersion:api.ODVersion){
const migrations = (await import("./migration.js")).migrations
migrations.sort((a,b) => {
const comparison = a.version.compare(b.version)
@@ -140,10 +186,10 @@ export async function loadAllAfterInitVersionMigrations(lastVersion:api.ODVersio
for (const migration of migrations){
if (migration.version.compare(lastVersion) == "higher"){
- const success = await migration.migrateAfterInit()
+ const success = await migration.migrateAfterStartup()
if (success) opendiscord.log("Migrated data to "+migration.version.toString()+"!","debug",[
{key:"success",value:success ? "true" : "false"},
- {key:"afterInit",value:"true"}
+ {key:"type",value:"after-startup"}
])
else throw new api.ODSystemError("Migration Error: Unable to migrate database & config to the new version of the bot.")
}
diff --git a/src/core/startup/migration.ts b/src/core/startup/migration.ts
index e90d808..a62139f 100644
--- a/src/core/startup/migration.ts
+++ b/src/core/startup/migration.ts
@@ -1,147 +1,351 @@
import { opendiscord, api, utilities } from "../../index.js"
+import fs from "fs"
+import path from "path"
export const migrations = [
//MIGRATE TO v4.0.0
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.0"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.0"),{}),
//MIGRATE TO v4.0.1
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.1"),async () => {},async () => {
- //AFTER INIT MIGRATION
-
- //add opendiscord:panel-message properties for all existing panels.
- const globalDatabase = opendiscord.databases.get("opendiscord:global")
- for (const panel of (await globalDatabase.getCategory("opendiscord:panel-update") ?? [])){
- globalDatabase.set("opendiscord:panel-message",panel.key,panel.value)
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.1"),{
+ afterStartupMigrate:async () => {
+ //add opendiscord:panel-message properties for all existing panels.
+ const globalDatabase = opendiscord.databases.get("opendiscord:global")
+ for (const panel of (await globalDatabase.getCategory("opendiscord:panel-update") ?? [])){
+ globalDatabase.set("opendiscord:panel-message",panel.key,panel.value)
+ }
}
}),
//MIGRATE TO v4.0.2
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.2"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.2"),{}),
//MIGRATE TO v4.0.3
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.3"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.3"),{}),
//MIGRATE TO v4.0.4
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.4"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.4"),{}),
//MIGRATE TO v4.0.5
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.5"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.5"),{}),
//MIGRATE TO v4.0.6
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.6"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.6"),{}),
//MIGRATE TO v4.0.7
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.7"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.0.7"),{}),
//MIGRATE TO v4.1.0
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.0"),async () => {},async () => {
- //AFTER INIT MIGRATION
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.0"),{
+ afterStartupMigrate:async () => {
+ //migrate config
+ const generalConfig = opendiscord.configs.get("opendiscord:general")
+ const optionConfig = opendiscord.configs.get("opendiscord:options")
- //migrate config
- const generalConfig = opendiscord.configs.get("opendiscord:general")
- const optionConfig = opendiscord.configs.get("opendiscord:options")
+ if (!generalConfig.data.status.state){
+ //only migrate config when it hasn't been done manually by the user.
- if (!generalConfig.data.status.state){
- //only migrate config when it hasn't been done manually by the user.
+ if (!generalConfig.data["_INFO"]) throw new api.ODSystemError("Couldn't find general.json '_INFO' category.")
+ generalConfig.data["_INFO"].version = "open-ticket-v4.1.0"
- if (!generalConfig.data._INFO) throw new api.ODSystemError("Couldn't find general.json '_INFO' category.")
- generalConfig.data._INFO.version = "open-ticket-v4.1.0"
+ if (!generalConfig.data.status) throw new api.ODSystemError("Couldn't find general.json 'status' category.")
+ generalConfig.data.status.mode = generalConfig.data.status["status"] ?? "online"
+ generalConfig.data.status.state = ""
+ delete generalConfig.data.status["status"]
- if (!generalConfig.data.status) throw new api.ODSystemError("Couldn't find general.json 'status' category.")
- generalConfig.data.status.mode = generalConfig.data.status["status"] ?? "online"
- generalConfig.data.status.state = ""
- delete generalConfig.data.status["status"]
+ if (!generalConfig.data["system"]) throw new api.ODSystemError("Couldn't find general.json 'system' category.")
+ generalConfig.data["system"].displayFieldsWithQuestions = false
+ generalConfig.data["system"].showGlobalAdminsInPanelRoles = false
+ generalConfig.data["system"].alwaysShowReason = false
+ generalConfig.data["system"].pinEmoji = "๐"
+ generalConfig.data["system"].askPriorityOnTicketCreation = false
+ generalConfig.data["system"].disableAutocloseAfterReopen = true
+ generalConfig.data["system"].autodeleteRequiresClosedTicket = true
+ generalConfig.data["system"].adminOnlyDeleteWithoutTranscript = true
+ generalConfig.data["system"].allowCloseBeforeMessage = false
+ generalConfig.data["system"].allowCloseBeforeAdminMessage = true
+ generalConfig.data["system"].pinFirstTicketMessage = false
- if (!generalConfig.data.system) throw new api.ODSystemError("Couldn't find general.json 'system' category.")
- generalConfig.data.system.displayFieldsWithQuestions = false
- generalConfig.data.system.showGlobalAdminsInPanelRoles = false
- generalConfig.data.system.alwaysShowReason = false
- generalConfig.data.system.pinEmoji = "๐"
- generalConfig.data.system.askPriorityOnTicketCreation = false
- generalConfig.data.system.disableAutocloseAfterReopen = true
- generalConfig.data.system.autodeleteRequiresClosedTicket = true
- generalConfig.data.system.adminOnlyDeleteWithoutTranscript = true
- generalConfig.data.system.allowCloseBeforeMessage = false
- generalConfig.data.system.allowCloseBeforeAdminMessage = true
- generalConfig.data.system.pinFirstTicketMessage = false
-
- generalConfig.data.system.channelTopic = {
- showOptionName:true,
- showOptionDescription:false,
- showOptionTopic:true,
- showPriority:false,
- showClosed:true,
- showClaimed:false,
- showPinned:false,
- showCreator:false,
- showParticipants:false
- }
-
- if (!generalConfig.data.system.permissions) throw new api.ODSystemError("Couldn't find general.json 'system.permissions' category.")
- generalConfig.data.system.permissions.transfer = "admin"
- generalConfig.data.system.permissions.topic = "admin"
- generalConfig.data.system.permissions.priority = "admin"
-
- if (!generalConfig.data.system.messages) throw new api.ODSystemError("Couldn't find general.json 'system.messages' category.")
- generalConfig.data.system.messages.transferring = {dm:false,logs:true}
- generalConfig.data.system.messages.topicChange = {dm:false,logs:true}
- generalConfig.data.system.messages.priorityChange = {dm:false,logs:true}
- generalConfig.data.system.messages.reactionRole = generalConfig.data.system.messages["roleAdding"] ?? {dm:false,logs:true}
- delete generalConfig.data.system.messages["roleAdding"]
- delete generalConfig.data.system.messages["roleRemoving"]
-
- for (const option of optionConfig.data){
- if (option.type != "ticket") continue
- option.channel.topic = option.channel["description"] ?? ""
- delete option.channel["description"]
-
- option.slowMode = {
- enabled:false,
- slowModeSeconds:20
+ generalConfig.data["system"].channelTopic = {
+ showOptionName:true,
+ showOptionDescription:false,
+ showOptionTopic:true,
+ showPriority:false,
+ showClosed:true,
+ showClaimed:false,
+ showPinned:false,
+ showCreator:false,
+ showParticipants:false
}
+
+ if (!generalConfig.data["system"].permissions) throw new api.ODSystemError("Couldn't find general.json 'system.permissions' category.")
+ generalConfig.data["system"].permissions.transfer = "admin"
+ generalConfig.data["system"].permissions.topic = "admin"
+ generalConfig.data["system"].permissions.priority = "admin"
+
+ if (!generalConfig.data["system"].messages) throw new api.ODSystemError("Couldn't find general.json 'system.messages' category.")
+ generalConfig.data["system"].messages.transferring = {dm:false,logs:true}
+ generalConfig.data["system"].messages.topicChange = {dm:false,logs:true}
+ generalConfig.data["system"].messages.priorityChange = {dm:false,logs:true}
+ generalConfig.data["system"].messages.reactionRole = generalConfig.data["system"].messages["roleAdding"] ?? {dm:false,logs:true}
+ delete generalConfig.data["system"].messages["roleAdding"]
+ delete generalConfig.data["system"].messages["roleRemoving"]
+
+ for (const option of optionConfig.data){
+ if (option.type != "ticket") continue
+ option.channel.topic = option.channel["description"] ?? ""
+ delete option.channel["description"]
+
+ option.slowMode = {
+ enabled:false,
+ slowModeSeconds:20
+ }
+ }
+
+ await generalConfig.save()
+ await optionConfig.save()
}
- await generalConfig.save()
- await optionConfig.save()
- }
+ //migrate database
+ const optionDatabase = opendiscord.databases.get("opendiscord:options")
+ const ticketDatabase = opendiscord.databases.get("opendiscord:tickets")
- //migrate database
- const optionDatabase = opendiscord.databases.get("opendiscord:options")
- const ticketDatabase = opendiscord.databases.get("opendiscord:tickets")
+ for (const option of (await optionDatabase.getCategory("opendiscord:used-option") ?? [])){
+ const optionData = option.value
+
+ const topicData = optionData.data.find((d) => d.id == "opendiscord:channel-description")
+ if (topicData) topicData.id = "opendiscord:channel-topic"
+ if (!optionData.data.find((d) => d.id == "opendiscord:slowmode-enabled")) optionData.data.push({id:"opendiscord:slowmode-enabled",value:false})
+ if (!optionData.data.find((d) => d.id == "opendiscord:slowmode-seconds")) optionData.data.push({id:"opendiscord:slowmode-seconds",value:20})
- for (const option of (await optionDatabase.getCategory("opendiscord:used-option") ?? [])){
- const optionData = option.value
-
- const topicData = optionData.data.find((d) => d.id == "opendiscord:channel-description")
- if (topicData) topicData.id = "opendiscord:channel-topic"
- if (!optionData.data.find((d) => d.id == "opendiscord:slowmode-enabled")) optionData.data.push({id:"opendiscord:slowmode-enabled",value:false})
- if (!optionData.data.find((d) => d.id == "opendiscord:slowmode-seconds")) optionData.data.push({id:"opendiscord:slowmode-seconds",value:20})
+ optionDatabase.set("opendiscord:used-option",option.key,optionData)
+ }
- optionDatabase.set("opendiscord:used-option",option.key,optionData)
- }
+ for (const ticket of (await ticketDatabase.getCategory("opendiscord:ticket") ?? [])){
+ const ticketData = ticket.value
+
+ if (!ticketData.data.find((d) => d.id == "opendiscord:previous-creators")) ticketData.data.push({id:"opendiscord:previous-creators",value:[]})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:reopened")) ticketData.data.push({id:"opendiscord:reopened",value:false})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:reopened-by")) ticketData.data.push({id:"opendiscord:reopened-by",value:null})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:reopened-on")) ticketData.data.push({id:"opendiscord:reopened-on",value:null})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:priority")) ticketData.data.push({id:"opendiscord:priority",value:-1})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:topic")) ticketData.data.push({id:"opendiscord:topic",value:""})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:message-sent")) ticketData.data.push({id:"opendiscord:message-sent",value:true})
+ if (!ticketData.data.find((d) => d.id == "opendiscord:admin-message-sent")) ticketData.data.push({id:"opendiscord:admin-message-sent",value:true})
- for (const ticket of (await ticketDatabase.getCategory("opendiscord:ticket") ?? [])){
- const ticketData = ticket.value
-
- if (!ticketData.data.find((d) => d.id == "opendiscord:previous-creators")) ticketData.data.push({id:"opendiscord:previous-creators",value:[]})
- if (!ticketData.data.find((d) => d.id == "opendiscord:reopened")) ticketData.data.push({id:"opendiscord:reopened",value:false})
- if (!ticketData.data.find((d) => d.id == "opendiscord:reopened-by")) ticketData.data.push({id:"opendiscord:reopened-by",value:null})
- if (!ticketData.data.find((d) => d.id == "opendiscord:reopened-on")) ticketData.data.push({id:"opendiscord:reopened-on",value:null})
- if (!ticketData.data.find((d) => d.id == "opendiscord:priority")) ticketData.data.push({id:"opendiscord:priority",value:-1})
- if (!ticketData.data.find((d) => d.id == "opendiscord:topic")) ticketData.data.push({id:"opendiscord:topic",value:""})
- if (!ticketData.data.find((d) => d.id == "opendiscord:message-sent")) ticketData.data.push({id:"opendiscord:message-sent",value:true})
- if (!ticketData.data.find((d) => d.id == "opendiscord:admin-message-sent")) ticketData.data.push({id:"opendiscord:admin-message-sent",value:true})
-
- ticketDatabase.set("opendiscord:ticket",ticket.key,ticketData)
+ ticketDatabase.set("opendiscord:ticket",ticket.key,ticketData)
+ }
}
}),
//MIGRATE TO v4.1.1
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.1"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.1"),{}),
//MIGRATE TO v4.1.2
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.2"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.2"),{}),
//MIGRATE TO v4.1.3
- new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.3"),async () => {},async () => {}),
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.1.3"),{}),
+
+ //MIGRATE TO v4.2.0
+ new api.ODVersionMigration(api.ODVersion.fromString("opendiscord:version","v4.2.0"),{
+ beforeStartupMigrate:async () => {
+ const isDevconfig = (process.argv.includes("--dev-config") || process.argv.includes("-dc"))
+
+ //transfer config files to .jsonc
+ const configDir = path.join(process.cwd(),(isDevconfig) ? "./devconfig/" : "./config/")
+ for (const file of fs.readdirSync(configDir).filter((f) => f.endsWith(".json"))){
+ try{
+ fs.copyFileSync(path.join(configDir,file),path.join(configDir,file.replace(".json",".jsonc")))
+ fs.rmSync(path.join(configDir,file))
+ }catch(err){
+ process.emit("uncaughtException",err)
+ }
+ }
+ },
+ afterStartupMigrate:async () => {
+ //migrate config
+ const generalConfig = opendiscord.configs.get("opendiscord:general")
+ const questionConfig = opendiscord.configs.get("opendiscord:questions")
+ const optionConfig = opendiscord.configs.get("opendiscord:options")
+ const panelConfig = opendiscord.configs.get("opendiscord:panels")
+ const transcriptConfig = opendiscord.configs.get("opendiscord:transcripts")
+
+ if (!generalConfig.data.ticketSystem){
+ //only migrate config when it hasn't been done manually by the user.
+
+ if (!generalConfig.data["_INFO"]) throw new api.ODSystemError("Couldn't find general.jsonc '_INFO' category.")
+ delete generalConfig.data["_INFO"]
+ generalConfig.data._CONFIG_VERSION = "open-ticket-v4.2.0"
+
+ if (!generalConfig.data["system"]) throw new api.ODSystemError("Couldn't find general.jsonc 'system' category.")
+ generalConfig.data.ticketSystem = generalConfig.data["system"]
+ generalConfig.data.ticketSystem.closeEmoji = "๐"
+ generalConfig.data.ticketSystem.askPriorityOnTicketCreation = true
+ generalConfig.data.ticketSystem.enableCreateTicketForOtherUser = true
+ delete generalConfig.data["system"]
+ generalConfig.data.logs = generalConfig.data.ticketSystem["logs"]
+ delete generalConfig.data.ticketSystem["logs"]
+ generalConfig.data.logs.logMessages = generalConfig.data.ticketSystem["messages"]
+ delete generalConfig.data.ticketSystem["messages"]
+ generalConfig.data.permissions = generalConfig.data.ticketSystem["permissions"]
+ delete generalConfig.data.ticketSystem["permissions"]
+ generalConfig.data.permissions.transcripts = "admin"
+
+ //closed category
+ const closedCategory = {enabled:false,categoryId:"DISCORD_CATEGORY_ID"}
+ for (const option of optionConfig.data){
+ if (option.type != "ticket") continue
+ if (option.channel["closedCategory"] && /^\d+$/.test(option.channel["closedCategory"])){
+ closedCategory.enabled = true
+ closedCategory.categoryId = option.channel["closedCategory"]
+ }
+ }
+ generalConfig.data.ticketSystem.closedCategory = closedCategory
+
+ //backup category
+ const backupCategory = {enabled:false,categoryId:"DISCORD_CATEGORY_ID"}
+ for (const option of optionConfig.data){
+ if (option.type != "ticket") continue
+ if (option.channel["backupCategory"] && /^\d+$/.test(option.channel["backupCategory"])){
+ backupCategory.enabled = true
+ backupCategory.categoryId = option.channel["backupCategory"]
+ }
+ }
+ generalConfig.data.ticketSystem.backupCategory = backupCategory
+
+ //claimed categories
+ const claimedCategories: {user:string,category:string}[] = []
+ for (const option of optionConfig.data){
+ if (option.type != "ticket" || !Array.isArray(option.channel["claimedCategory"])) continue
+ for (const {user,category} of option.channel["claimedCategory"]){
+ if (typeof user == "string" && typeof category == "string" && /^\d+$/.test(user) && /^\d+$/.test(category) && !claimedCategories.find((c) => c.user == user)){
+ claimedCategories.push({user,category})
+ }
+ }
+ }
+ generalConfig.data.ticketSystem.claimedCategories = claimedCategories
+
+
+ //delete properties from options.jsonc
+ for (const option of optionConfig.data){
+ if (option.type != "ticket") continue
+ delete option.channel["closedCategory"]
+ delete option.channel["backupCategory"]
+ delete option.channel["claimedCategory"]
+ }
+
+ //update panels config:
+ for (const panel of panelConfig.data){
+ panel.settings.maximumButtonsPerRow = 5
+ }
+
+ //update questions config:
+ for (const question of questionConfig.data){
+ if (question.type !== "paragraph" && question.type !== "short") continue
+ question.description = ""
+ }
+
+ //add new sub-panel option example (for users to try)
+ optionConfig.data.push({
+ id:"example-sub-panel",
+ name:"Example Sub-Panel",
+ description:"This is an example of how to implement a sub-panel in Open Ticket.",
+ type:"sub-panel",
+
+ button:{
+ color:"gray",
+ label:"Sub-Panel Example",
+ emoji:"๐"
+ },
+ subPanelId:panelConfig.data[0]?.id ?? "example-panel"
+ })
+
+ //add new question examples (for users to try)
+ questionConfig.data.push(
+ {
+ id:"example-dropdown-question",
+ name:"Example Dropdown Question",
+ description:"This is a dropdown question.",
+ type:"dropdown",
+ required:false,
+
+ placeholder:"Choose your answer...",
+ choices:[
+ {title:"Choice A",description:"Apple",emoji:"๐"},
+ {title:"Choice B",description:"Banana",emoji:"๐"},
+ {title:"Choice C",description:"Orange",emoji:"๐"},
+ {title:"Choice D",description:"Kiwi",emoji:"๐ฅ"}
+ ]
+ },
+ {
+ id:"example-radio-question",
+ name:"Example Radio Question",
+ description:"This is a radio select question.",
+ type:"radio-select",
+ required:true,
+
+ choices:[
+ {title:"Choice A",description:"Up",selectedByDefault:false},
+ {title:"Choice B",description:"Down",selectedByDefault:false},
+ {title:"Choice C",description:"Left",selectedByDefault:false},
+ {title:"Choice D",description:"Right",selectedByDefault:false}
+ ]
+ },
+ {
+ id:"example-checkbox-question",
+ name:"Example Checkbox Question",
+ description:"This is a checkbox select question.",
+ type:"checkbox-select",
+ required:true,
+
+ limits:{
+ enabled:false,
+ min:0,
+ max:10
+ },
+ choices:[
+ {title:"Choice A",description:"Happiness",selectedByDefault:false},
+ {title:"Choice B",description:"Anger",selectedByDefault:false},
+ {title:"Choice C",description:"Sadness",selectedByDefault:false},
+ {title:"Choice D",description:"Fear",selectedByDefault:false}
+ ]
+ },
+ {
+ id:"example-text-display-question",
+ type:"text-display",
+ textContents:"This is a text display. It isn't a question, but allows you to display a text, explaination or details."
+ }
+ )
+
+ await generalConfig.save()
+ await questionConfig.save()
+ await optionConfig.save()
+ await panelConfig.save()
+ await transcriptConfig.save()
+ }
+
+ //migrate database
+ const optionDatabase = opendiscord.databases.get("opendiscord:options")
+ const ticketDatabase = opendiscord.databases.get("opendiscord:tickets")
+
+ for (const option of (await optionDatabase.getCategory("opendiscord:used-option") ?? [])){
+ const optionData = option.value
+ optionData.data = optionData.data.filter((data) => (
+ data.id !== "opendiscord:channel-category-closed" &&
+ data.id !== "pendiscord:channel-category-backup" &&
+ data.id !== "opendiscord:channel-categories-claimed"
+ ))
+
+ optionDatabase.set("opendiscord:used-option",option.key,optionData)
+ }
+
+ for (const ticket of (await ticketDatabase.getCategory("opendiscord:ticket") ?? [])){
+ const ticketData = ticket.value
+ if (!ticketData.data.find((d) => d.id == "opendiscord:channel-renamed")) ticketData.data.push({id:"opendiscord:channel-renamed",value:null})
+
+ ticketDatabase.set("opendiscord:ticket",ticket.key,ticketData)
+ }
+ }
+ }),
]
\ No newline at end of file
diff --git a/src/data/framework/checkerLoader.ts b/src/data/framework/checkerLoader.ts
index 2a26a05..182f814 100644
--- a/src/data/framework/checkerLoader.ts
+++ b/src/data/framework/checkerLoader.ts
@@ -17,7 +17,7 @@ export async function loadAllConfigCheckerFunctions(){
}
export async function loadAllConfigCheckerTranslations(){
- if ((generalConfig && generalConfig.data.system && generalConfig.data.system.useTranslatedConfigChecker) ? generalConfig.data.system.useTranslatedConfigChecker : false){
+ if ((generalConfig && generalConfig.data.ticketSystem && generalConfig.data.ticketSystem.useTranslatedConfigChecker) ? generalConfig.data.ticketSystem.useTranslatedConfigChecker : false){
registerDefaultCheckerSystemTranslations(opendiscord.checkers.translation,opendiscord.languages) //translate checker system text
registerDefaultCheckerMessageTranslations(opendiscord.checkers.translation,opendiscord.languages) //translate checker messages
registerDefaultCheckerCustomTranslations(opendiscord.checkers.translation,opendiscord.languages) //translate custom checker messages
@@ -25,7 +25,7 @@ export async function loadAllConfigCheckerTranslations(){
}
//GLOBAL FUNCTIONS
-export const registerDefaultCheckerSystemTranslations = (tm:api.ODMappedCheckerTranslationRegister,lm:api.ODMappedLanguageManager) => {
+export function registerDefaultCheckerSystemTranslations(tm:api.ODMappedCheckerTranslationRegister,lm:api.ODMappedLanguageManager){
//SYSTEM
tm.quickTranslate(lm,"checker.system.headerOpenTicket","other","opendiscord:header-projectname") //OPEN TICKET
tm.quickTranslate(lm,"checker.system.typeError","other","opendiscord:type-error") // [ERROR] (ignore)
@@ -42,7 +42,7 @@ export const registerDefaultCheckerSystemTranslations = (tm:api.ODMappedCheckerT
tm.quickTranslate(lm,"checker.system.dataMessages","other","opendiscord:data-message") // message
}
-export const registerDefaultCheckerMessageTranslations = (tm:api.ODMappedCheckerTranslationRegister,lm:api.ODMappedLanguageManager) => {
+export function registerDefaultCheckerMessageTranslations(tm:api.ODMappedCheckerTranslationRegister,lm:api.ODMappedLanguageManager){
//STRUCTURES
tm.quickTranslate(lm,"checker.messages.invalidType","message","opendiscord:invalid-type") // This property needs to be the type: {0}!
tm.quickTranslate(lm,"checker.messages.propertyMissing","message","opendiscord:property-missing") // The property {0} is missing from this object!
@@ -117,7 +117,7 @@ export const registerDefaultCheckerMessageTranslations = (tm:api.ODMappedChecker
tm.quickTranslate(lm,"checker.messages.idNonExistent","message","opendiscord:id-non-existent") // The id {0} doesn't exist!
}
-export const registerDefaultCheckerCustomTranslations = (tm:api.ODMappedCheckerTranslationRegister,lm:api.ODMappedLanguageManager) => {
+export function registerDefaultCheckerCustomTranslations(tm:api.ODMappedCheckerTranslationRegister,lm:api.ODMappedLanguageManager){
//CUSTOM
tm.quickTranslate(lm,"checker.messages.invalidLanguage","message","opendiscord:invalid-language") // This is an invalid language!
tm.quickTranslate(lm,"checker.messages.invalidButton","message","opendiscord:invalid-button") // This button needs to have at least an {0} or {1}!
@@ -128,13 +128,30 @@ export const registerDefaultCheckerCustomTranslations = (tm:api.ODMappedCheckerT
}
//UTILITY FUNCTIONS
-const createMsgStructure = (id:api.ODValidId,displayName:string) => {
+/**Get the panel ids from `panels.jsonc` before it has been checked by the config checker. */
+function getUnsafePanelIds(): string[] {
+ const panelsConfig = opendiscord.configs.get("opendiscord:panels")
+ if (!Array.isArray(panelsConfig.data)) return []
+
+ const panelIds: string[] = []
+ for (const unsafePanel of panelsConfig.data){
+ if (unsafePanel["id"]) panelIds.push(unsafePanel["id"])
+ }
+ return panelIds
+}
+
+function createMsgStructure(id:api.ODValidId,displayName:string){
return new api.ODCheckerObjectStructure(id,{children:[
{key:"dm",checker:new api.ODCheckerBooleanStructure("opendiscord:msg-dm",{cliInitDefaultValue:false,cliDisplayName:"DM Enabled",cliDisplayDescription:"Will this action be sent in DM to the creator of the ticket?"})},
{key:"logs",checker:new api.ODCheckerBooleanStructure("opendiscord:msg-logs",{cliInitDefaultValue:true,cliDisplayName:"Logs Enabled",cliDisplayDescription:"Will this action be sent in the Discord log channel?"})},
],cliDisplayName:displayName,cliDisplayDescription:"Configure which places this action gets logged/sent to."})
}
-const createTicketEmbedStructure = (id:api.ODValidId) => {
+
+function createPermissionStructure(id:api.ODValidId,displayName:string){
+ return new api.ODCheckerCustomStructure_DiscordId(id,"role",false,["admin","everyone","none"],{cliDisplayName:displayName,cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})
+}
+
+function createTicketEmbedStructure(id:api.ODValidId){
return new api.ODCheckerEnabledObjectStructure(id,{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure(id,{children:[
{key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:ticket-embed-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable the embed of this message."})},
{key:"title",checker:new api.ODCheckerStringStructure("opendiscord:ticket-embed-text",{maxLength:256,cliDisplayName:"Title",cliDisplayDescription:"The title of this embed."})},
@@ -151,14 +168,16 @@ const createTicketEmbedStructure = (id:api.ODValidId) => {
{key:"timestamp",checker:new api.ODCheckerBooleanStructure("opendiscord:ticket-embed-timestamp",{cliDisplayName:"Timestamp",cliDisplayDescription:"Add a timestamp to the embed."})}
],cliDisplayName:"Message Embed",cliDisplayDescription:"Configure the embed of this message."}),cliInitDefaultValue:{enabled:false,title:"",description:"",customColor:"",image:"",thumbnail:"",fields:[],timestamp:false},cliDisplayName:"Message Embed",cliDisplayDescription:"Configure the embed of this message."})
}
-const createTicketPingStructure = (id:api.ODValidId) => {
+
+function createTicketPingStructure(id:api.ODValidId){
return new api.ODCheckerObjectStructure(id,{children:[
{key:"@here",checker:new api.ODCheckerBooleanStructure("opendiscord:ticket-ping-here",{cliDisplayName:"@here Ping",cliDisplayDescription:"Enable/disable an '@here' ping."})},
{key:"@everyone",checker:new api.ODCheckerBooleanStructure("opendiscord:ticket-ping-everyone",{cliDisplayName:"@everyone Ping",cliDisplayDescription:"Enable/disable an '@everyone' ping."})},
{key:"custom",checker:new api.ODCheckerCustomStructure_DiscordIdArray("opendiscord:ticket-ping-custom","role",[],{allowDoubles:false,cliDisplayPropertyName:"custom role id",cliDisplayName:"Custom Role Ping",cliDisplayDescription:"Choose your own roles to ping in this message."},{cliDisplayName:"Custom Role",cliDisplayDescription:"The discord role ID of a custom mention/ping."})},
],cliInitDefaultValue:{"@here":true,"@everyone":false,custom:[],cliDisplayName:"Message Pings",cliDisplayDescription:"Configure the pings/mentions of this message."}})
}
-const createPanelEmbedStructure = (id:api.ODValidId) => {
+
+function createPanelEmbedStructure(id:api.ODValidId){
return new api.ODCheckerEnabledObjectStructure(id,{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure(id,{children:[
{key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:panel-embed-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable the embed of this panel."})},
{key:"title",checker:new api.ODCheckerStringStructure("opendiscord:panel-embed-text",{maxLength:256,cliDisplayName:"Title",cliDisplayDescription:"The title of this embed."})},
@@ -188,23 +207,19 @@ function loadFromEnv(){
//STRUCTURES
export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendiscord:general",{children:[
//INFO
- {key:"_INFO",cliHideInEditMode:true,checker:new api.ODCheckerObjectStructure("opendiscord:info",{children:[
- {key:"support",checker:new api.ODCheckerStringStructure("opendiscord:info-support",{choices:["https://otdocs.dj-dj.be"]})},
- {key:"discord",checker:new api.ODCheckerStringStructure("opendiscord:info-discord",{choices:["https://discord.dj-dj.be"]})},
- {key:"version",checker:new api.ODCheckerStringStructure("opendiscord:info-version",{custom(checker,value,locationTrace,locationId,locationDocs) {
- const lt = checker.locationTraceDeref(locationTrace)
-
- if (typeof value != "string") return false
- else if (value != "open-ticket-"+opendiscord.versions.get("opendiscord:version").toString()){
- checker.createMessage("opendiscord:invalid-version","warning","The version specified in your config does not match! Make sure you have updated the config to the latest version!",lt,null,[],locationId,locationDocs)
- return false
- }else return true
- },})},
- ]})},
+ {key:"_CONFIG_VERSION",cliHideInEditMode:true,checker:new api.ODCheckerStringStructure("opendiscord:config-version",{custom(checker,value,locationTrace,locationId,locationDocs) {
+ const lt = checker.locationTraceDeref(locationTrace)
+
+ if (typeof value != "string") return false
+ else if (value != "open-ticket-"+opendiscord.versions.get("opendiscord:version").toString()){
+ checker.createMessage("opendiscord:invalid-version","warning","The version specified in your config does not match! Make sure you have updated the config to the latest version!",lt,null,[],locationId,locationDocs)
+ return false
+ }else return true
+ },})},
//BASIC
{key:"token",checker:(loadFromEnv()) ? new api.ODCheckerStringStructure("opendiscord:token-disabled",{cliDisplayName:"Token",cliDisplayDescription:"The token of your discord bot."}) : new api.ODCheckerCustomStructure_DiscordToken("opendiscord:token",{cliDisplayName:"Token",cliDisplayDescription:"The token of your discord bot."})},
- {key:"tokenFromENV",checker:new api.ODCheckerBooleanStructure("opendiscord:token-env",{cliDisplayName:"Token From ENV",cliDisplayDescription:"Use the token from the .env file instead of general.json."})},
+ {key:"tokenFromENV",checker:new api.ODCheckerBooleanStructure("opendiscord:token-env",{cliDisplayName:"Token From ENV",cliDisplayDescription:"Use the token from the .env file instead of general.jsonc."})},
{key:"mainColor",checker:new api.ODCheckerCustomStructure_HexColor("opendiscord:main-color",true,false,{cliDisplayName:"Main Color",cliDisplayDescription:"The main color of your bot, used in almost all embeds."})},
{key:"language",checker:new api.ODCheckerStringStructure("opendiscord:language",{
custom:(checker,value,locationTrace,locationId,locationDocs) => {
@@ -230,6 +245,7 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis
{key:"status",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:status",{
property:"enabled",
enabledValue:true,
+ ignoreCheckIfDisabled:true,
checker:new api.ODCheckerObjectStructure("opendiscord:status",{children:[
{key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:status-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable the status. When disabled, the bot will be online without any status."})},
{key:"type",checker:new api.ODCheckerStringStructure("opendiscord:status-type",{choices:["listening","watching","playing","custom"],cliDisplayName:"Type",cliDisplayDescription:"The type of status: Listening, Watching, Playing or Custom."})},
@@ -241,8 +257,8 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis
cliDisplayDescription:"Manage the status of the bot."
})},
- //SYSTEM
- {key:"system",checker:new api.ODCheckerObjectStructure("opendiscord:system",{children:[
+ //TICKET SYSTEM
+ {key:"ticketSystem",checker:new api.ODCheckerObjectStructure("opendiscord:ticket-system",{children:[
{key:"preferSlashOverText",checker:new api.ODCheckerBooleanStructure("opendiscord:prefer-slash-over-text",{cliDisplayName:"Prefer Slash Over Text",cliDisplayDescription:"Prefer displaying slash commands over text commands in help menus."})},
{key:"sendErrorOnUnknownCommand",checker:new api.ODCheckerBooleanStructure("opendiscord:send-error-on-unknown-command",{cliDisplayName:"Send Error On Unknown Command",cliDisplayDescription:"Send an error when using the text-command prefix without a valid command."})},
{key:"questionFieldsInCodeBlock",checker:new api.ODCheckerBooleanStructure("opendiscord:question-fields-in-code-block",{cliDisplayName:"Questions Fields In Code Blocks",cliDisplayDescription:"Display question fields in code blocks instead of plain text."})},
@@ -252,7 +268,8 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis
{key:"useRedErrorEmbeds",checker:new api.ODCheckerBooleanStructure("opendiscord:use-red-error-embeds",{cliDisplayName:"Use Red Error Embeds",cliDisplayDescription:"Display all error messages with a red border instead of the default color of the bot."})},
{key:"alwaysShowReason",checker:new api.ODCheckerBooleanStructure("opendiscord:always-show-reason",{cliDisplayName:"Always Show Reason",cliDisplayDescription:"Always show the reason field in embeds, even when there is no reason provided."})},
{key:"emojiStyle",checker:new api.ODCheckerStringStructure("opendiscord:emoji-style",{choices:["before","after","double","disabled"],cliDisplayName:"Emoji Style",cliDisplayDescription:"Choose how the bot will display emojis in message titles. (Visit docs for more info)"})},
- {key:"pinEmoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:pin-emoji",1,1,false,{cliDisplayName:"Pin Emoji",cliDisplayDescription:"The emoji used when pinning tickets. This is '๐' by default."})},
+ {key:"pinEmoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:pin-emoji",0,1,false,{cliDisplayName:"Pin Emoji",cliDisplayDescription:"The emoji used when pinning tickets. This is '๐' by default. Leave empty to disable."})},
+ {key:"closeEmoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:close-emoji",0,1,false,{cliDisplayName:"Pin Emoji",cliDisplayDescription:"The emoji used when closing tickets. This is '๐' by default. Leave empty to disable."})},
{key:"replyOnTicketCreation",checker:new api.ODCheckerBooleanStructure("opendiscord:reply-on-ticket-creation",{cliDisplayName:"Reply On Ticket Creation",cliDisplayDescription:"When enabled, the bot will send an ephemeral reply in the channel of the panel when creating a ticket."})},
{key:"replyOnReactionRole",checker:new api.ODCheckerBooleanStructure("opendiscord:reply-on-reaction-role",{cliDisplayName:"Reply On Reaction Role",cliDisplayDescription:"When enabled, the bot will send an ephemeral reply in the channel of the panel when using a role button."})},
@@ -272,11 +289,7 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis
{key:"enableTicketDeleteButtons",checker:new api.ODCheckerBooleanStructure("opendiscord:enable-ticket-delete-buttons",{cliDisplayName:"Enable Ticket Delete Buttons",cliDisplayDescription:"Enable/disable buttons for deleting a ticket. Be aware that this doesn't disable the command!"})},
{key:"enableTicketActionWithReason",checker:new api.ODCheckerBooleanStructure("opendiscord:enable-ticket-action-with-reason",{cliDisplayName:"Enable Ticket Action With Reason",cliDisplayDescription:"Enable/disable buttons to write an additional reason for all ticket actions."})},
{key:"enableDeleteWithoutTranscript",checker:new api.ODCheckerBooleanStructure("opendiscord:enable-delete-without-transcript",{cliDisplayName:"Enable Delete Without Transcript",cliDisplayDescription:"Enable/disable the ability to delete tickets without a transcript."})},
-
- {key:"logs",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:system-logs",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:system-logs",{children:[
- {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:logs-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable discord logs in a discord channel."})},
- {key:"channel",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:logs-channel","channel",false,[],{cliDisplayName:"Log Channel",cliDisplayDescription:"The ID of the discord channel to log messages to. You can configure the messages somewhere else."})},
- ],cliDisplayName:"Discord Logs",cliDisplayDescription:"Manage everything related to logs in a discord channel."}),cliDisplayName:"Discord Logs",cliDisplayDescription:"Manage everything related to logs in a discord channel."})},
+ {key:"enableCreateTicketForOtherUser",checker:new api.ODCheckerBooleanStructure("opendiscord:enable-create-for-other-user",{cliDisplayName:"Enable Create ticket for other user",cliDisplayDescription:"Enable/disable the ability for admins to create a ticket for another user."})},
{key:"limits",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:limits",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:limits",{children:[
{key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:limits-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable global limits."})},
@@ -287,42 +300,66 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis
{key:"channelTopic",checker:new api.ODCheckerObjectStructure("opendiscord:channel-topic",{children:[
{key:"showOptionName",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-name",{cliDisplayName:"Show Option Name",cliDisplayDescription:"Show the option name in the channel topic."})},
{key:"showOptionDescription",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-description",{cliDisplayName:"Show Option Description",cliDisplayDescription:"Show the option description in the channel topic."})},
- {key:"showOptionTopic",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-topic",{cliDisplayName:"Show Option Topic",cliDisplayDescription:"Show the option topic text in the channel topic (configured in the options.json config)."})},
+ {key:"showOptionTopic",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-option-topic",{cliDisplayName:"Show Option Topic",cliDisplayDescription:"Show the option topic text in the channel topic (configured in the options.jsonc config)."})},
{key:"showPriority",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-priority",{cliDisplayName:"Show Priority",cliDisplayDescription:"Show the current priority in the channel topic (auto-updated)."})},
{key:"showClosed",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-closed",{cliDisplayName:"Show Closed Status",cliDisplayDescription:"Show the current close/reopen status in the channel topic (auto-updated)."})},
{key:"showClaimed",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-claimed",{cliDisplayName:"Show Claimed Status",cliDisplayDescription:"Show the current claim status in the channel topic (auto-updated)."})},
{key:"showPinned",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-pinned",{cliDisplayName:"Show Pinned Status",cliDisplayDescription:"Show the current pin status in the channel topic (auto-updated)."})},
{key:"showCreator",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-creator",{cliDisplayName:"Show Creator",cliDisplayDescription:"Show the creator of the ticket in the channel topic (auto-updated on transfer)."})},
{key:"showParticipants",checker:new api.ODCheckerBooleanStructure("opendiscord:topic-show-participants",{cliDisplayName:"Show Participants",cliDisplayDescription:"Show the first 5 participants of the ticket in the channel topic (auto-updated)."})},
-
],cliDisplayName:"Channel Topic",cliDisplayDescription:"Manage stats and text of ticket channel topics."})},
- {key:"permissions",checker:new api.ODCheckerObjectStructure("opendiscord:system-permissions",{children:[
- {key:"help",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-help","role",false,["admin","everyone","none"],{cliDisplayName:"Help",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"panel",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-panel","role",false,["admin","everyone","none"],{cliDisplayName:"Panel",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"ticket",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-ticket","role",false,["admin","everyone","none"],{cliDisplayName:"Ticket",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"close",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-close","role",false,["admin","everyone","none"],{cliDisplayName:"Close",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"delete",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-delete","role",false,["admin","everyone","none"],{cliDisplayName:"Delete",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"reopen",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-reopen","role",false,["admin","everyone","none"],{cliDisplayName:"Reopen",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"claim",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-claim","role",false,["admin","everyone","none"],{cliDisplayName:"Claim",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"unclaim",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-unclaim","role",false,["admin","everyone","none"],{cliDisplayName:"Unclaim",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"pin",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-pin","role",false,["admin","everyone","none"],{cliDisplayName:"Pin",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"unpin",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-unpin","role",false,["admin","everyone","none"],{cliDisplayName:"Unpin",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"move",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-move","role",false,["admin","everyone","none"],{cliDisplayName:"Move",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"rename",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-rename","role",false,["admin","everyone","none"],{cliDisplayName:"Rename",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"add",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-add","role",false,["admin","everyone","none"],{cliDisplayName:"Add User",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"remove",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-remove","role",false,["admin","everyone","none"],{cliDisplayName:"Remove User",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"blacklist",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-blacklist","role",false,["admin","everyone","none"],{cliDisplayName:"Blacklist",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"stats",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-stats","role",false,["admin","everyone","none"],{cliDisplayName:"Stats",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"clear",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-clear","role",false,["admin","everyone","none"],{cliDisplayName:"Clear Tickets",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"autoclose",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-autoclose","role",false,["admin","everyone","none"],{cliDisplayName:"Autoclose",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"autodelete",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-autodelete","role",false,["admin","everyone","none"],{cliDisplayName:"Autodelete",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"transfer",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-transfer","role",false,["admin","everyone","none"],{cliDisplayName:"Transfer",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"topic",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-topic","role",false,["admin","everyone","none"],{cliDisplayName:"Topic",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- {key:"priority",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:permissions-priority","role",false,["admin","everyone","none"],{cliDisplayName:"Priority",cliHideDescriptionInParent:true,cliDisplayDescription:"Set the permissions to 'everyone' for everyone, 'admin' for admin only, 'none' to disable or a custom discord role ID."})},
- ],cliDisplayName:"Permissions",cliDisplayDescription:"Manage all button & command permissions in the bot. (Visit docs for more info)"})},
+ {key:"closedCategory",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:closed-category",{property:"enabled",enabledValue:true,ignoreCheckIfDisabled:true,checker:new api.ODCheckerObjectStructure("opendiscord:closed-category",{children:[
+ {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:closed-category-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable closed category."})},
+ {key:"categoryId",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:closed-category","category",true,[],{cliDisplayName:"Closed Category",cliDisplayDescription:"An additional category where tickets will be moved to when closed."})},
+ ],cliDisplayName:"Closed Category",cliDisplayDescription:"An additional category where tickets will be moved to when closed."}),cliDisplayName:"Closed Category",cliDisplayDescription:"An additional category where tickets will be moved to when closed."})},
+
+ {key:"backupCategory",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:backup-category",{property:"enabled",enabledValue:true,ignoreCheckIfDisabled:true,checker:new api.ODCheckerObjectStructure("opendiscord:backup-category",{children:[
+ {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:backup-category-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable backup category."})},
+ {key:"categoryId",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:backup-category","category",true,[],{cliDisplayName:"Backup Category",cliDisplayDescription:"An additional category where tickets will be created in when the original category is full (50 channels)."})},
+ ],cliDisplayName:"Backup Category",cliDisplayDescription:"An additional category where tickets will be created in when the original category is full (50 channels)."}),cliDisplayName:"Backup Category",cliDisplayDescription:"An additional category where tickets will be created in when the original category is full (50 channels)."})},
+
+ {key:"claimedCategories",checker:new api.ODCheckerArrayStructure("opendiscord:claimed-categories",{allowDoubles:false,allowedTypes:["object"],cliDisplayPropertyName:"claim category",propertyChecker:new api.ODCheckerObjectStructure("opendiscord:claimed-category",{children:[
+ {key:"user",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:claimed-user","user",false,[],{cliDisplayName:"User",cliDisplayDescription:"The discord user ID of a ticket claimer."})},
+ {key:"category",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:claimed-category","category",false,[],{cliDisplayName:"Category",cliDisplayDescription:"The discord category ID to move the ticket to."})}
+ ],cliDisplayName:"Claimed Category",cliDisplayDescription:"Move claimed tickets to the matching channel category of the user that claimed the ticket."}),cliDisplayName:"Claimed Categories",cliDisplayDescription:"Move claimed tickets to the matching channel category of the user that claimed the ticket."})},
+
+ ],cliDisplayName:"Ticket System",cliDisplayDescription:"Configure the 'Open Ticket' ticket system."})},
- {key:"messages",checker:new api.ODCheckerObjectStructure("opendiscord:system-permissions",{children:[
+ //COMMAND PERMISSIONS
+ {key:"permissions",checker:new api.ODCheckerObjectStructure("opendiscord:permissions",{children:[
+ {key:"help",checker:createPermissionStructure("opendiscord:permissions-help","Help")},
+ {key:"panel",checker:createPermissionStructure("opendiscord:permissions-panel","Panel")},
+ {key:"ticket",checker:createPermissionStructure("opendiscord:permissions-ticket","Ticket")},
+ {key:"close",checker:createPermissionStructure("opendiscord:permissions-close","Close")},
+ {key:"delete",checker:createPermissionStructure("opendiscord:permissions-delete","Delete")},
+ {key:"reopen",checker:createPermissionStructure("opendiscord:permissions-reopen","Reopen")},
+ {key:"claim",checker:createPermissionStructure("opendiscord:permissions-claim","Claim")},
+ {key:"unclaim",checker:createPermissionStructure("opendiscord:permissions-unclaim","Unclaim")},
+ {key:"pin",checker:createPermissionStructure("opendiscord:permissions-pin","Pin")},
+ {key:"unpin",checker:createPermissionStructure("opendiscord:permissions-unpin","Unpin")},
+ {key:"move",checker:createPermissionStructure("opendiscord:permissions-move","Move")},
+ {key:"rename",checker:createPermissionStructure("opendiscord:permissions-rename","Rename")},
+ {key:"add",checker:createPermissionStructure("opendiscord:permissions-add","Add User")},
+ {key:"remove",checker:createPermissionStructure("opendiscord:permissions-remove","Remove User")},
+ {key:"blacklist",checker:createPermissionStructure("opendiscord:permissions-blacklist","Blacklist")},
+ {key:"stats",checker:createPermissionStructure("opendiscord:permissions-stats","Stats")},
+ {key:"clear",checker:createPermissionStructure("opendiscord:permissions-clear","Clear Tickets")},
+ {key:"autoclose",checker:createPermissionStructure("opendiscord:permissions-autoclose","Autoclose")},
+ {key:"autodelete",checker:createPermissionStructure("opendiscord:permissions-autodelete","Autodelete")},
+ {key:"transfer",checker:createPermissionStructure("opendiscord:permissions-transfer","Transfer")},
+ {key:"topic",checker:createPermissionStructure("opendiscord:permissions-topic","Topic")},
+ {key:"priority",checker:createPermissionStructure("opendiscord:permissions-priority","Priority")},
+ {key:"transcripts",checker:createPermissionStructure("opendiscord:permissions-transcripts","Transcripts History")},
+ ],cliDisplayName:"Permissions",cliDisplayDescription:"Manage all button & command permissions in the bot. (Visit docs for more info)"})},
+
+ //LOGS
+ {key:"logs",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:logs",{property:"enabled",enabledValue:true,ignoreCheckIfDisabled:true,checker:new api.ODCheckerObjectStructure("opendiscord:logs",{children:[
+ {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:logs-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable discord logs in a discord channel."})},
+ {key:"channel",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:logs-channel","channel",false,[],{cliDisplayName:"Log Channel",cliDisplayDescription:"The log message channel ID."})},
+
+ //LOG MESSAGES
+ {key:"logMessages",checker:new api.ODCheckerObjectStructure("opendiscord:log-messages",{children:[
{key:"creation",checker:createMsgStructure("opendiscord:msg-creation","Ticket Created")},
{key:"closing",checker:createMsgStructure("opendiscord:msg-closing","Ticket Closed")},
{key:"deleting",checker:createMsgStructure("opendiscord:msg-deleting","Ticket Deleted")},
@@ -338,22 +375,23 @@ export const defaultGeneralStructure = new api.ODCheckerObjectStructure("opendis
{key:"topicChange",checker:createMsgStructure("opendiscord:msg-topic-change","Topic Changed")},
{key:"priorityChange",checker:createMsgStructure("opendiscord:msg-priority-change","Priority Changed")},
{key:"reactionRole",checker:createMsgStructure("opendiscord:msg-reaction-role","Reaction Role")},
- ],cliDisplayName:"Messages",cliDisplayDescription:"Manage all messages & DM's for each action of the bot. (Visit docs for more info)"})},
- ],cliDisplayName:"System",cliDisplayDescription:"Configure everything related to the ticket system."})}
+ ],cliDisplayName:"Log Messages",cliDisplayDescription:"Manage all messages & DM's for each action of the bot. (Visit docs for more info)"})},
+ ],cliDisplayName:"Discord Logs",cliDisplayDescription:"Manage the 'Open Ticket' logs channel."}),cliDisplayName:"Discord Logs",cliDisplayDescription:"Manage the 'Open Ticket' logs channel."})},
+
],cliDisplayName:"General",cliDisplayDescription:"General settings for the bot."})
export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendiscord:options",{allowedTypes:["object"],cliDisplayPropertyName:"option",propertyChecker:new api.ODCheckerObjectSwitchStructure("opendiscord:options",{objects:[
//TICKET
{name:"Ticket",priority:0,properties:[{key:"type",value:"ticket"}],checker:new api.ODCheckerObjectStructure("opendiscord:ticket",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],cliInitSkipKeys:["readonlyAdmins"],children:[
- {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:ticket-id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this ticket option. Used in panels."})},
- {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:ticket-name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this ticket option."})},
- {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:ticket-description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this ticket option."})},
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this ticket option. Used in panels."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this ticket option."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this ticket option."})},
//TICKET BUTTON
- {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:ticket-button",{children:[
- {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:ticket-button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
- {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:ticket-button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
- {key:"color",checker:new api.ODCheckerStringStructure("opendiscord:ticket-button-color",{choices:["gray","red","green","blue"],cliDisplayName:"Color",cliDisplayDescription:"The color of the button. Does not apply when using a dropdown panel."})},
+ {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:button",{children:[
+ {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"color",checker:new api.ODCheckerStringStructure("opendiscord:button-color",{choices:["gray","red","green","blue"],cliDisplayName:"Color",cliDisplayDescription:"The color of the button. Does not apply when using a dropdown panel."})},
],custom:(checker,value,locationTrace,locationId,locationDocs) => {
const lt = checker.locationTraceDeref(locationTrace)
//check if emoji & label exists
@@ -369,7 +407,7 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc
{key:"ticketAdmins",checker:new api.ODCheckerCustomStructure_DiscordIdArray("opendiscord:ticket-ticket-admins","role",[],{allowDoubles:false,cliDisplayPropertyName:"ticket admin role",cliDisplayName:"Ticket Admin Roles",cliDisplayDescription:"A list of role IDs that are only able to interact with this ticket option."},{cliDisplayName:"Ticket Admin Role",cliDisplayDescription:"The discord role ID of a ticket admin."})},
{key:"readonlyAdmins",checker:new api.ODCheckerCustomStructure_DiscordIdArray("opendiscord:ticket-readonly-admins","role",[],{allowDoubles:false,cliInitDefaultValue:[],cliDisplayPropertyName:"read-only ticket admin role",cliDisplayName:"Readonly Admin Roles",cliDisplayDescription:"A list of role IDs that are only able to read this ticket option."},{cliDisplayName:"Readonly Admin Role",cliDisplayDescription:"The discord role ID of a readonly admin."})},
{key:"allowCreationByBlacklistedUsers",checker:new api.ODCheckerBooleanStructure("opendiscord:ticket-allow-blacklisted-users",{cliDisplayName:"Allow Creation By Blacklisted Users",cliDisplayDescription:"When enabled, the blacklist doesn't apply to this ticket option/type and users are still able to create a ticket."})},
- {key:"questions",checker:new api.ODCheckerCustomStructure_UniqueIdArray("opendiscord:option-questions","openticket","question-ids","question-ids-used",{allowDoubles:false,maxLength:5,cliDisplayPropertyName:"question",cliDisplayName:"Questions",cliDisplayDescription:"A list of valid question IDs to ask before creating this ticket."},{cliDisplayName:"Question ID",cliDisplayDescription:"A valid question ID from the questions.json config.",cliAutocompleteFunc:async () => {
+ {key:"questions",checker:new api.ODCheckerCustomStructure_UniqueIdArray("opendiscord:option-questions","openticket","question-ids","question-ids-used",{allowDoubles:false,maxLength:5,cliDisplayPropertyName:"question",cliDisplayName:"Questions",cliDisplayDescription:"A list of valid question IDs to ask before creating this ticket."},{cliDisplayName:"Question ID",cliDisplayDescription:"A valid question ID from the questions.jsonc config.",cliAutocompleteFunc:async () => {
const uncheckedRawData = opendiscord.configs.get("opendiscord:questions").data
if (!Array.isArray(uncheckedRawData)) return null
const idList = uncheckedRawData.filter((option) => typeof option == "object" && typeof option["id"] == "string").map((option) => option.id)
@@ -380,15 +418,8 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc
{key:"channel",checker:new api.ODCheckerObjectStructure("opendiscord:ticket-channel",{cliInitSkipKeys:["backupCategory","claimedCategory"],children:[
{key:"prefix",checker:new api.ODCheckerStringStructure("opendiscord:ticket-channel-prefix",{maxLength:25,regex:/^[^\s]*$/,cliDisplayName:"Prefix",cliDisplayDescription:"The prefix of the name of the ticket channel. (e.g. 'question-')"})},
{key:"suffix",checker:new api.ODCheckerStringStructure("opendiscord:ticket-channel-suffix",{choices:["user-name","user-nickname","user-id","random-number","random-hex","counter-dynamic","counter-fixed"],cliDisplayName:"Suffix",cliDisplayDescription:"The suffix mode to use. The number/text will be appended after the prefix."})},
-
{key:"category",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-category","category",true,[],{cliDisplayName:"Category",cliDisplayDescription:"The category the ticket will be created in. Leave empty for no category."})},
- {key:"closedCategory",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-closed-category","category",true,[],{cliDisplayName:"Closed Category",cliDisplayDescription:"An additional category where the ticket will be moved to when closed."})},
- {key:"backupCategory",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-backup-category","category",true,[],{cliDisplayName:"Backup Category",cliDisplayDescription:"An additional category where the ticket will be created in when the primary category is full (50 channels)."})},
- {key:"claimedCategory",checker:new api.ODCheckerArrayStructure("opendiscord:ticket-channel-claimed-category",{allowDoubles:false,allowedTypes:["object"],cliDisplayPropertyName:"claim category",propertyChecker:new api.ODCheckerObjectStructure("opendiscord:ticket-channel-claimed-category",{children:[
- {key:"user",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-claimed-user","user",false,[],{cliDisplayName:"User",cliDisplayDescription:"A discord user ID of the ticket claimer."})},
- {key:"category",checker:new api.ODCheckerCustomStructure_DiscordId("opendiscord:ticket-channel-claimed-category","category",false,[],{cliDisplayName:"Category",cliDisplayDescription:"A discord category ID to move the ticket to."})}
- ],cliDisplayName:"Claimed Category",cliDisplayDescription:"A collection of a user ID and a category ID. The ticket will be moved to the category when this user claims the ticket."}),cliDisplayName:"Claimed Categories",cliDisplayDescription:"Add categories to move the ticket to when a user claims a ticket."})},
- {key:"topic",checker:new api.ODCheckerStringStructure("opendiscord:ticket-channel-topic",{cliDisplayName:"Channel Topic",cliDisplayDescription:"The topic text of the ticket channel. Visible in the discord client when general.json 'channelTopic'.'showOptionTopic' is enabled."})},
+ {key:"topic",checker:new api.ODCheckerStringStructure("opendiscord:ticket-channel-topic",{cliDisplayName:"Channel Topic",cliDisplayDescription:"The topic text of the ticket channel. Visible in the discord client when general.jsonc 'channelTopic'.'showOptionTopic' is enabled."})},
],cliDisplayName:"Channel",cliDisplayDescription:"Manage all settings related to the ticket channel and categories."})},
//DM MESSAGE
@@ -445,14 +476,14 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc
//WEBSITE
{name:"Website",priority:0,properties:[{key:"type",value:"website"}],checker:new api.ODCheckerObjectStructure("opendiscord:website",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
- {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:website-id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this website option. Used in panels."})},
- {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:website-name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this website option."})},
- {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:website-description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this website option."})},
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this website option. Used in panels."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this website option."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this website option."})},
//WEBSITE BUTTON
- {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:ticket-button",{children:[
- {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:ticket-button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
- {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:ticket-button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:button",{children:[
+ {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
],custom:(checker,value,locationTrace,locationId,locationDocs) => {
const lt = checker.locationTraceDeref(locationTrace)
//check if emoji & label exists
@@ -470,15 +501,15 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc
//REACTION ROLES
{name:"Reaction Role",priority:0,properties:[{key:"type",value:"role"}],checker:new api.ODCheckerObjectStructure("opendiscord:role",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
- {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:role-id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this role option. Used in panels."})},
- {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:role-name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this role option."})},
- {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:role-description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this role option."})},
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this role option. Used in panels."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this role option."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this role option."})},
//ROLE BUTTON
- {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:ticket-button",{children:[
- {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:ticket-button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
- {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:ticket-button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
- {key:"color",checker:new api.ODCheckerStringStructure("opendiscord:ticket-button-color",{choices:["gray","red","green","blue"],cliDisplayName:"Color",cliDisplayDescription:"The color of the button. Does not apply when using a dropdown panel."})},
+ {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:button",{children:[
+ {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"color",checker:new api.ODCheckerStringStructure("opendiscord:button-color",{choices:["gray","red","green","blue"],cliDisplayName:"Color",cliDisplayDescription:"The color of the button. Does not apply when using a dropdown panel."})},
],custom:(checker,value,locationTrace,locationId,locationDocs) => {
const lt = checker.locationTraceDeref(locationTrace)
//check if emoji & label exists
@@ -496,13 +527,52 @@ export const defaultOptionsStructure = new api.ODCheckerArrayStructure("opendisc
{key:"removeRolesOnAdd",checker:new api.ODCheckerCustomStructure_DiscordIdArray("opendiscord:role-remove-roles","role",[],{allowDoubles:false,cliDisplayPropertyName:"role",cliDisplayName:"Remove Roles On Add",cliDisplayDescription:"An additional list of roles to remove when the roles of this option are added. (Can be used to select between roles)"},{cliDisplayName:"Remove Role",cliDisplayDescription:"The discord role ID you want to remove when other roles are added."})},
{key:"addOnMemberJoin",checker:new api.ODCheckerBooleanStructure("opendiscord:role-add-on-join",{cliDisplayName:"Add On Member Join",cliDisplayDescription:"Automatically add these roles to a user when joining the server."})},
],cliDisplayName:"Reaction Role Option",cliDisplayDescription:"Manage all settings of this reaction role option."})},
+
+ //SUB-PANEL
+ {name:"Reaction Role",priority:0,properties:[{key:"type",value:"sub-panel"}],checker:new api.ODCheckerObjectStructure("opendiscord:sub-panel",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:id","openticket","option-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this sub-panel option. Used in panels."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:name",{minLength:2,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this sub-panel option."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:description",{maxLength:256,cliDisplayName:"Description",cliDisplayDescription:"The description of this sub-panel option."})},
+
+ //SUB-PANEL BUTTON
+ {key:"button",checker:new api.ODCheckerObjectStructure("opendiscord:button",{children:[
+ {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:button-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"label",checker:new api.ODCheckerStringStructure("opendiscord:button-label",{maxLength:80,cliDisplayName:"Label",cliDisplayDescription:"The label of the button. At least 1 of 2 (emoji/label) must be provided."})},
+ {key:"color",checker:new api.ODCheckerStringStructure("opendiscord:button-color",{choices:["gray","red","green","blue"],cliDisplayName:"Color",cliDisplayDescription:"The color of the button. Does not apply when using a dropdown panel."})},
+ ],custom:(checker,value,locationTrace,locationId,locationDocs) => {
+ const lt = checker.locationTraceDeref(locationTrace)
+ //check if emoji & label exists
+ if (typeof value != "object") return false
+ else if (value && value["emoji"].length < 1 && value["label"].length < 1){
+ //label & emoji are both empty
+ checker.createMessage("opendiscord:invalid-button","error",`This button needs to have at least an "emoji" or "label"!`,lt,null,[`"emoji"`,`"label"`],locationId,locationDocs)
+ return false
+ }else return true
+ },cliDisplayName:"Button",cliDisplayDescription:"Customise the button layout of this sub-panel option."})},
+
+ //SUB-PANEL SETTINGS
+ {key:"subPanelId",checker:new api.ODCheckerStringStructure("ot-footers:panel-id",{custom:(checker,value,locationTrace,locationId,locationDocs) => {
+ const lt = checker.locationTraceDeref(locationTrace)
+ if (typeof value != "string") return false
+
+ if (getUnsafePanelIds().includes(value)){
+ //exists
+ return true
+ }else{
+ //doesn't exist
+ checker.createMessage("opendiscord:id-non-existent","error",`The panel id "${value}" doesn't exist!`,lt,null,[`"${value}"`],locationId,locationDocs)
+ return false
+ }
+ }})},
+ ],cliDisplayName:"Sub-Panel Option",cliDisplayDescription:"Manage all settings of this sub-panel option."})},
+
],cliDisplayName:"Option",cliDisplayDescription:"Manage an option of one of the 3 types: ticket, website, role."}),cliDisplayName:"Options",cliDisplayDescription:"A list of all options in the bot. Here you can add, modify & remove ticket types, website buttons & reaction roles!"})
export const defaultPanelsStructure = new api.ODCheckerArrayStructure("opendiscord:panels",{allowedTypes:["object"],cliDisplayPropertyName:"panel",propertyChecker:new api.ODCheckerObjectStructure("opendiscord:panels",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","dropdown"],children:[
{key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:panel-id","openticket","panel-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this panel. Used in the /panel command."})},
{key:"name",checker:new api.ODCheckerStringStructure("opendiscord:panel-name",{minLength:3,maxLength:50,cliDisplayName:"Name",cliDisplayDescription:"The name of this panel."})},
{key:"dropdown",checker:new api.ODCheckerBooleanStructure("opendiscord:panel-dropdown",{cliDisplayName:"Dropdown",cliDisplayDescription:"Decide whether to use buttons or a dropdown in the panel. Dropdowns only support options of the 'ticket' type!"})},
- {key:"options",checker:new api.ODCheckerCustomStructure_UniqueIdArray("opendiscord:panel-options","openticket","option-ids","option-ids-used",{allowDoubles:false,minLength:1,maxLength:25,cliDisplayPropertyName:"option",cliDisplayName:"Options",cliDisplayDescription:"A list of valid option IDs to show in this panel."},{cliDisplayName:"Option ID",cliDisplayDescription:"A valid option ID from the options.json config.",cliAutocompleteFunc:async () => {
+ {key:"options",checker:new api.ODCheckerCustomStructure_UniqueIdArray("opendiscord:panel-options","openticket","option-ids","option-ids-used",{allowDoubles:false,minLength:1,maxLength:25,cliDisplayPropertyName:"option",cliDisplayName:"Options",cliDisplayDescription:"A list of valid option IDs to show in this panel."},{cliDisplayName:"Option ID",cliDisplayDescription:"A valid option ID from the options.jsonc config.",cliAutocompleteFunc:async () => {
const uncheckedRawData = opendiscord.configs.get("opendiscord:options").data
if (!Array.isArray(uncheckedRawData)) return null
const idList = uncheckedRawData.filter((option) => typeof option == "object" && typeof option["id"] == "string").map((option) => option.id)
@@ -516,6 +586,8 @@ export const defaultPanelsStructure = new api.ODCheckerArrayStructure("opendisco
//SETTINGS
{key:"settings",checker:new api.ODCheckerObjectStructure("opendiscord:panel-settings",{cliInitSkipKeys:["dropdownPlaceholder","describeOptionsCustomTitle"],children:[
{key:"dropdownPlaceholder",checker:new api.ODCheckerStringStructure("opendiscord:panel-settings-placeholder",{maxLength:100,cliInitDefaultValue:"Create a ticket!",cliDisplayName:"Dropdown Placeholder",cliDisplayDescription:"Configure the text displayed in the dropdown when nothing is selected."})},
+ {key:"maximumButtonsPerRow",checker:new api.ODCheckerNumberStructure("opendiscord:panel-settings-row-amount",{min:1,max:5,floatAllowed:false,cliInitDefaultValue:5,cliDisplayName:"Maximum Buttons Per Row",cliDisplayDescription:"Set the maximum amount of buttons in a single row before starting a new one."})},
+
{key:"enableMaxTicketsWarningInText",checker:new api.ODCheckerBooleanStructure("opendiscord:panel-settings-maxtickets-text",{cliDisplayName:"Enable Max Tickets Warning (Text)",cliDisplayDescription:"Enable/disable the warning which shows how many tickets you can create in the text contents of the panel."})},
{key:"enableMaxTicketsWarningInEmbed",checker:new api.ODCheckerBooleanStructure("opendiscord:panel-settings-maxtickets-embed",{cliDisplayName:"Enable Max Tickets Warning (Embed)",cliDisplayDescription:"Enable/disable the warning which shows how many tickets you can create in the embed of the panel."})},
@@ -527,20 +599,95 @@ export const defaultPanelsStructure = new api.ODCheckerArrayStructure("opendisco
],cliDisplayName:"Settings",cliDisplayDescription:"Manage additional settings & customisability for this panel."})},
],cliDisplayName:"Panel",cliDisplayDescription:"Manage, customise and configure a panel to your preference."}),cliDisplayName:"Panels",cliDisplayDescription:"A list of all panels in the bot. Here you can add, modify & remove existing panels or customise them to your preference."})
-export const defaultQuestionsStructure = new api.ODCheckerArrayStructure("opendiscord:questions",{allowedTypes:["object"],cliDisplayPropertyName:"question",propertyChecker:new api.ODCheckerObjectStructure("opendiscord:questions",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
- {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
- {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
- {key:"type",checker:new api.ODCheckerStringStructure("opendiscord:question-type",{choices:["short","paragraph"],cliDisplayName:"Type",cliDisplayDescription:"The type of this question (short/paragraph)."})},
-
- {key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
- {key:"placeholder",checker:new api.ODCheckerStringStructure("opendiscord:question-placeholder",{maxLength:100,cliDisplayName:"Placeholder",cliDisplayDescription:"The placeholder to show in the field when nothing has been written yet."})},
-
- {key:"length",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:question-length",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:question-length",{children:[
- {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:question-length-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable length validation for this question."})},
- {key:"min",checker:new api.ODCheckerNumberStructure("opendiscord:question-length-min",{min:0,max:1024,negativeAllowed:false,floatAllowed:false,cliDisplayName:"Min Length",cliDisplayDescription:"The minimum amount of characters required."})},
- {key:"max",checker:new api.ODCheckerNumberStructure("opendiscord:question-length-max",{min:1,max:1024,negativeAllowed:false,floatAllowed:false,cliInitDefaultValue:100,cliDisplayName:"Max Length",cliDisplayDescription:"The maximum amount of characters allowed."})},
- ],cliDisplayName:"Length Validation",cliDisplayDescription:"Add length validation to the question. This way, the contents must be at least/most ... characters."}),cliDisplayName:"Length Validation",cliDisplayDescription:"Add length validation to the question. This way, the contents must be at least/most ... characters."})},
-],cliDisplayName:"Question",cliDisplayDescription:"Manage, customise and configure a question to your preference."}),cliDisplayName:"Questions",cliDisplayDescription:"A list of all questions in the bot. Here you can add, modify & remove existing questions or customise them to your preference."})
+export const defaultQuestionsStructure = new api.ODCheckerArrayStructure("opendiscord:questions",{allowedTypes:["object"],cliDisplayPropertyName:"question",propertyChecker:new api.ODCheckerObjectSwitchStructure("opendiscord:options",{objects:[
+ //SHORT QUESTION
+ {name:"Short Question",priority:0,properties:[{key:"type",value:"short"}],checker:new api.ODCheckerObjectStructure("opendiscord:short-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:question-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this question."})},
+ {key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
+
+ {key:"placeholder",checker:new api.ODCheckerStringStructure("opendiscord:question-placeholder",{maxLength:100,cliDisplayName:"Placeholder",cliDisplayDescription:"The placeholder to show in the field when nothing has been written yet."})},
+
+ {key:"length",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:question-length",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:question-length",{children:[
+ {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:question-length-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable length validation for this question."})},
+ {key:"min",checker:new api.ODCheckerNumberStructure("opendiscord:question-length-min",{min:0,max:1024,negativeAllowed:false,floatAllowed:false,cliDisplayName:"Min Length",cliDisplayDescription:"The minimum amount of characters required."})},
+ {key:"max",checker:new api.ODCheckerNumberStructure("opendiscord:question-length-max",{min:1,max:1024,negativeAllowed:false,floatAllowed:false,cliInitDefaultValue:100,cliDisplayName:"Max Length",cliDisplayDescription:"The maximum amount of characters allowed."})},
+ ],cliDisplayName:"Length Validation",cliDisplayDescription:"Add length validation to the question. This way, the contents must be at least/most ... characters."}),cliDisplayName:"Length Validation",cliDisplayDescription:"Add length validation to the question. This way, the contents must be at least/most ... characters."})},
+ ],cliDisplayName:"Short Question",cliDisplayDescription:"Manage, customise and configure the short question to your preference."})},
+
+ //PARAGRAPH QUESTION
+ {name:"Paragraph Question",priority:0,properties:[{key:"type",value:"paragraph"}],checker:new api.ODCheckerObjectStructure("opendiscord:paragraph-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:question-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this question."})},
+ {key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
+
+ {key:"placeholder",checker:new api.ODCheckerStringStructure("opendiscord:question-placeholder",{maxLength:100,cliDisplayName:"Placeholder",cliDisplayDescription:"The placeholder to show in the field when nothing has been written yet."})},
+
+ {key:"length",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:question-length",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:question-length",{children:[
+ {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:question-length-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable length validation for this question."})},
+ {key:"min",checker:new api.ODCheckerNumberStructure("opendiscord:question-length-min",{min:0,max:1024,negativeAllowed:false,floatAllowed:false,cliDisplayName:"Min Length",cliDisplayDescription:"The minimum amount of characters required."})},
+ {key:"max",checker:new api.ODCheckerNumberStructure("opendiscord:question-length-max",{min:1,max:1024,negativeAllowed:false,floatAllowed:false,cliInitDefaultValue:100,cliDisplayName:"Max Length",cliDisplayDescription:"The maximum amount of characters allowed."})},
+ ],cliDisplayName:"Length Validation",cliDisplayDescription:"Add length validation to the question. This way, the contents must be at least/most ... characters."}),cliDisplayName:"Length Validation",cliDisplayDescription:"Add length validation to the question. This way, the contents must be at least/most ... characters."})},
+ ],cliDisplayName:"Paragraph Question",cliDisplayDescription:"Manage, customise and configure the paragraph question to your preference."})},
+
+ //TEXT DISPLAY QUESTION
+ {name:"Text Display Question",priority:0,properties:[{key:"type",value:"text-display"}],checker:new api.ODCheckerObjectStructure("opendiscord:text-display-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this text display. Used in ticket options."})},
+ {key:"textContents",checker:new api.ODCheckerStringStructure("opendiscord:text-contents",{minLength:1,maxLength:2048,cliDisplayName:"Text Contents",cliDisplayDescription:"The text contents to show in the modal."})},
+ ],cliDisplayName:"Text Display Question",cliDisplayDescription:"Manage, customise and configure the text display question to your preference."})},
+
+ //DROPDOWN QUESTION
+ {name:"Dropdown Question",priority:0,properties:[{key:"type",value:"dropdown"}],checker:new api.ODCheckerObjectStructure("opendiscord:dropdown-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:question-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this question."})},
+ {key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
+
+ {key:"placeholder",checker:new api.ODCheckerStringStructure("opendiscord:question-placeholder",{maxLength:100,cliDisplayName:"Placeholder",cliDisplayDescription:"The placeholder to show in the field when nothing has been written yet."})},
+ {key:"choices",checker:new api.ODCheckerArrayStructure("opendiscord:choices",{allowedTypes:["object"],minLength:1,maxLength:25,propertyChecker:new api.ODCheckerObjectStructure("opendiscord:choice",{children:[
+ {key:"title",checker:new api.ODCheckerStringStructure("opendiscord:choice-title",{minLength:1,maxLength:100,cliDisplayName:"Title",cliDisplayDescription:"The title of this choice."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:choice-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this choice."})},
+ {key:"emoji",checker:new api.ODCheckerCustomStructure_EmojiString("opendiscord:choice-emoji",0,1,true,{cliDisplayName:"Emoji",cliDisplayDescription:"The emoji of the choice. Leave empty to disable."})}
+ ],cliDisplayName:"Choice",cliDisplayDescription:"A choice for this dropdown question."}),cliDisplayName:"Choices",cliDisplayPropertyName:"choice",cliDisplayDescription:"Manage all available choices of this dropdown question."})}
+ ],cliDisplayName:"Dropdown Question",cliDisplayDescription:"Manage, customise and configure the dropdown question to your preference."})},
+
+ //RADIO SELECT QUESTION
+ {name:"Radio Select Question",priority:0,properties:[{key:"type",value:"radio-select"}],checker:new api.ODCheckerObjectStructure("opendiscord:radio-select-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:question-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this question."})},
+ {key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
+
+ {key:"choices",checker:new api.ODCheckerArrayStructure("opendiscord:choices",{allowedTypes:["object"],minLength:2,maxLength:10,propertyChecker:new api.ODCheckerObjectStructure("opendiscord:choice",{children:[
+ {key:"title",checker:new api.ODCheckerStringStructure("opendiscord:choice-title",{minLength:1,maxLength:100,cliDisplayName:"Title",cliDisplayDescription:"The title of this choice."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:choice-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this choice."})},
+ {key:"selectedByDefault",checker:new api.ODCheckerBooleanStructure("opendiscord:choice-default",{cliDisplayName:"Selected By Default",cliDisplayDescription:"Should this choice be selected by default?"})}
+ ],cliDisplayName:"Choice",cliDisplayDescription:"A choice for this radio select question."}),cliDisplayName:"Choices",cliDisplayPropertyName:"choice",cliDisplayDescription:"Manage all available choices of this radio select question."})}
+ ],cliDisplayName:"Radio Select Question",cliDisplayDescription:"Manage, customise and configure the radio select question to your preference."})},
+
+ //CHECKBOX SELECT QUESTION
+ {name:"Checkbox Select Question",priority:0,properties:[{key:"type",value:"checkbox-select"}],checker:new api.ODCheckerObjectStructure("opendiscord:checkbox-select-question",{cliDisplayKeyInParentArray:"name",cliDisplayAdditionalKeysInParentArray:["id","type"],children:[
+ {key:"id",checker:new api.ODCheckerCustomStructure_UniqueId("opendiscord:question-id","openticket","question-ids",{regex:/^[A-Za-z0-9-รฉรจรงร รชรขรดรปรฎ]+$/,minLength:3,maxLength:40,cliDisplayName:"Id",cliDisplayDescription:"The id of this question. Used in ticket options."})},
+ {key:"name",checker:new api.ODCheckerStringStructure("opendiscord:question-name",{minLength:3,maxLength:45,cliDisplayName:"Name",cliDisplayDescription:"The name of this question."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:question-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this question."})},
+ {key:"required",checker:new api.ODCheckerBooleanStructure("opendiscord:question-required",{cliDisplayName:"Required",cliDisplayDescription:"Is this question required? If not, it can be left empty."})},
+
+ {key:"limits",checker:new api.ODCheckerEnabledObjectStructure("opendiscord:amount",{property:"enabled",enabledValue:true,checker:new api.ODCheckerObjectStructure("opendiscord:amount",{children:[
+ {key:"enabled",checker:new api.ODCheckerBooleanStructure("opendiscord:amount-enabled",{cliDisplayName:"Enabled",cliDisplayDescription:"Enable/disable checking the min/max amount of required checkboxes."})},
+ {key:"min",checker:new api.ODCheckerNumberStructure("opendiscord:amount-min",{min:0,max:10,floatAllowed:false,cliDisplayName:"Min Amount",cliDisplayDescription:"The minimum amount of checkboxes required."})},
+ {key:"max",checker:new api.ODCheckerNumberStructure("opendiscord:amount-max",{min:1,max:10,floatAllowed:false,cliInitDefaultValue:10,cliDisplayName:"Max Amount",cliDisplayDescription:"The maximum amount of checkboxes allowed."})},
+ ],cliDisplayName:"Checkbox Limits",cliDisplayDescription:"Verify the minimum or maximum amount of checkboxes required."}),cliDisplayName:"Checkbox Limits",cliDisplayDescription:"Verify the minimum or maximum amount of checkboxes required."})},
+
+ {key:"choices",checker:new api.ODCheckerArrayStructure("opendiscord:choices",{allowedTypes:["object"],minLength:1,maxLength:10,propertyChecker:new api.ODCheckerObjectStructure("opendiscord:choice",{children:[
+ {key:"title",checker:new api.ODCheckerStringStructure("opendiscord:choice-title",{minLength:1,maxLength:100,cliDisplayName:"Title",cliDisplayDescription:"The title of this choice."})},
+ {key:"description",checker:new api.ODCheckerStringStructure("opendiscord:choice-description",{maxLength:100,cliDisplayName:"Description",cliDisplayDescription:"The description of this choice."})},
+ {key:"selectedByDefault",checker:new api.ODCheckerBooleanStructure("opendiscord:choice-default",{cliDisplayName:"Selected By Default",cliDisplayDescription:"Should this choice be selected by default?"})}
+ ],cliDisplayName:"Choice",cliDisplayDescription:"A choice for this checkbox select question."}),cliDisplayName:"Choices",cliDisplayPropertyName:"choice",cliDisplayDescription:"Manage all available choices of this checkbox select question."})}
+ ],cliDisplayName:"Checkbox Select Question",cliDisplayDescription:"Manage, customise and configure the checkbox select question to your preference."})},
+
+],cliDisplayName:"Question",cliDisplayDescription:"Manage a question of one of the 6 types: short, paragraph, text-display, dropdown, radio-select, checkbox-select."}),cliDisplayName:"Questions",cliDisplayDescription:"A list of all questions in the bot. Here you can add, modify & remove existing questions or customise them to your preference."})
export const defaultTranscriptsStructure = new api.ODCheckerObjectStructure("opendiscord:transcripts",{children:[
//GENERAL
diff --git a/src/data/framework/codeLoader.ts b/src/data/framework/codeLoader.ts
index 8a0f32f..af57c14 100644
--- a/src/data/framework/codeLoader.ts
+++ b/src/data/framework/codeLoader.ts
@@ -30,7 +30,7 @@ export async function loadCommandErrorHandlingCode(){
error.msg.channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-option-invalid").build("text",{guild:error.msg.guild,channel:error.msg.channel,user:error.msg.author,error})).message)
}else if (error.type == "missing_option"){
error.msg.channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-option-missing").build("text",{guild:error.msg.guild,channel:error.msg.channel,user:error.msg.author,error})).message)
- }else if (error.type == "unknown_command" && generalConfig.data.system.sendErrorOnUnknownCommand){
+ }else if (error.type == "unknown_command" && generalConfig.data.ticketSystem.sendErrorOnUnknownCommand){
error.msg.channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-unknown-command").build("text",{guild:error.msg.guild,channel:error.msg.channel,user:error.msg.author,error})).message)
}
})
@@ -459,7 +459,7 @@ const loadAutoCode = () => {
if (lastMessage){
//ticket has last message
const disableOnClaim = ticket.option.get("opendiscord:autodelete-disable-claim").value && ticket.get("opendiscord:claimed").value
- const disableWhenNotClosed = generalConfig.data.system.autodeleteRequiresClosedTicket && !ticket.get("opendiscord:closed").value
+ const disableWhenNotClosed = generalConfig.data.ticketSystem.autodeleteRequiresClosedTicket && !ticket.get("opendiscord:closed").value
const enabled = (disableOnClaim || disableWhenNotClosed) ? false : ticket.get("opendiscord:autodelete-enabled").value
const days = ticket.get("opendiscord:autodelete-days").value
@@ -490,7 +490,7 @@ const loadAutoCode = () => {
if (!channel) return
//ticket has been created by this user
const disableOnClaim = ticket.option.get("opendiscord:autodelete-disable-claim").value && ticket.get("opendiscord:claimed").value
- const disableWhenNotClosed = generalConfig.data.system.autodeleteRequiresClosedTicket && !ticket.get("opendiscord:closed").value
+ const disableWhenNotClosed = generalConfig.data.ticketSystem.autodeleteRequiresClosedTicket && !ticket.get("opendiscord:closed").value
const enabled = (disableOnClaim || disableWhenNotClosed || !ticket.get("opendiscord:autodelete-enabled").value) ? false : ticket.option.get("opendiscord:autodelete-enable-leave")
if (enabled){
diff --git a/src/data/framework/commandLoader.ts b/src/data/framework/commandLoader.ts
index d6df28d..ec97ed0 100644
--- a/src/data/framework/commandLoader.ts
+++ b/src/data/framework/commandLoader.ts
@@ -14,8 +14,8 @@ export async function loadAllSlashCommands(){
if (!generalConfig.data.slashCommands) return
const allowedCommands: string[] = []
- for (const key in generalConfig.data.system.permissions){
- if (generalConfig.data.system.permissions[key] != "none") allowedCommands.push(key)
+ for (const key in generalConfig.data.permissions){
+ if (generalConfig.data.permissions[key] != "none") allowedCommands.push(key)
}
//HELP
@@ -87,7 +87,7 @@ export async function loadAllSlashCommands(){
}))
//DELETE
- if (allowedCommands.includes("delete") && generalConfig.data.system.enableDeleteWithoutTranscript) commands.add(new api.ODSlashCommand("opendiscord:delete",{
+ if (allowedCommands.includes("delete") && generalConfig.data.ticketSystem.enableDeleteWithoutTranscript) commands.add(new api.ODSlashCommand("opendiscord:delete",{
type:act.ChatInput,
name:"delete",
description:lang.getTranslation("commands.delete"),
@@ -654,8 +654,8 @@ export async function loadAllTextCommands(){
})
const allowedCommands: string[] = []
- for (const key in generalConfig.data.system.permissions){
- if (generalConfig.data.system.permissions[key] != "none") allowedCommands.push(key)
+ for (const key in generalConfig.data.permissions){
+ if (generalConfig.data.permissions[key] != "none") allowedCommands.push(key)
}
//HELP
diff --git a/src/data/framework/configLoader.ts b/src/data/framework/configLoader.ts
index a977e24..d845586 100644
--- a/src/data/framework/configLoader.ts
+++ b/src/data/framework/configLoader.ts
@@ -5,121 +5,63 @@ export async function loadAllConfigs(){
const devconfigFlag = opendiscord.flags.get("opendiscord:dev-config")
const isDevconfig = devconfigFlag ? devconfigFlag.value : false
- opendiscord.configs.add(new api.ODJsonConfig("opendiscord:general","general.json",(isDevconfig) ? "./devconfig/" : "./config/",defaultGeneralFormatter))
- opendiscord.configs.add(new api.ODJsonConfig("opendiscord:questions","questions.json",(isDevconfig) ? "./devconfig/" : "./config/",defaultQuestionsFormatter))
- opendiscord.configs.add(new api.ODJsonConfig("opendiscord:options","options.json",(isDevconfig) ? "./devconfig/" : "./config/",defaultOptionsFormatter))
- opendiscord.configs.add(new api.ODJsonConfig("opendiscord:panels","panels.json",(isDevconfig) ? "./devconfig/" : "./config/",defaultPanelsFormatter))
- opendiscord.configs.add(new api.ODJsonConfig("opendiscord:transcripts","transcripts.json",(isDevconfig) ? "./devconfig/" : "./config/",defaultTranscriptsFormatter))
+ opendiscord.configs.add(new api.ODJsonCommentsConfig("opendiscord:general","general.jsonc",(isDevconfig) ? "./devconfig/" : "./config/",defaultGeneralFormatter))
+ opendiscord.configs.add(new api.ODJsonCommentsConfig("opendiscord:questions","questions.jsonc",(isDevconfig) ? "./devconfig/" : "./config/",defaultQuestionsFormatter))
+ opendiscord.configs.add(new api.ODJsonCommentsConfig("opendiscord:options","options.jsonc",(isDevconfig) ? "./devconfig/" : "./config/",defaultOptionsFormatter))
+ opendiscord.configs.add(new api.ODJsonCommentsConfig("opendiscord:panels","panels.jsonc",(isDevconfig) ? "./devconfig/" : "./config/",defaultPanelsFormatter))
+ opendiscord.configs.add(new api.ODJsonCommentsConfig("opendiscord:transcripts","transcripts.jsonc",(isDevconfig) ? "./devconfig/" : "./config/",defaultTranscriptsFormatter))
}
//FORMATTERS
-export const defaultGeneralFormatter = new fjs.ObjectFormatter(null,true,[
- new fjs.ObjectFormatter("_INFO",true,[
- new fjs.PropertyFormatter("support"),
- new fjs.PropertyFormatter("discord"),
- new fjs.PropertyFormatter("version"),
- ]),
+export const defaultGeneralFormatter = new fjs.TopLevelCommentFormatter(new fjs.MultiCommentFormatter([
+ "Hi there! Thank you for installing Open Ticket.",
+ "----------------------------------------------",
+ "If you need any assistance with configuring the bot,",
+ "feel free to use the documentation or join our Discord server:",
+ "https://otdocs.dj-dj.be",
+ "https://discord.dj-dj.be",
+ "----------------------------------------------",
+ "SETUP:",
+ "1. Install the required dependencies using the command: \"npm install\"",
+ "2. Configure the bot in one of the following ways:",
+ " a. (easy) Using the Quick Setup CLI Tool",
+ " b. (difficult) Using the JSON files in `./config/`",
+ "",
+ "Start the Quick Setup CLI Tool using the command: \"npm run setup\"",
+ "After configuration, start the bot using the command: \"npm start\"",
+ "",
+ "Good luck! DJj123dj & contributors",
+ ].join("\n")),new fjs.ObjectFormatter(null,true,[
+ new fjs.PropertyFormatter("_CONFIG_VERSION"),
new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Load the bot token from .env or the \"token\" field below. Leave \"token\" empty if using \"tokenFromENV\"."),
new fjs.PropertyFormatter("token"),
new fjs.PropertyFormatter("tokenFromENV"),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("mainColor"),
- new fjs.PropertyFormatter("language"),
- new fjs.PropertyFormatter("prefix"),
+ new fjs.PropertyFormatter("mainColor",new fjs.SingleCommentFormatter("Hex color used in most embeds")),
+ new fjs.PropertyFormatter("language",new fjs.SingleCommentFormatter("Visit README.md for list")),
+ new fjs.PropertyFormatter("prefix",new fjs.SingleCommentFormatter("Prefix used in text commands")),
new fjs.PropertyFormatter("serverId"),
- new fjs.ArrayFormatter("globalAdmins",false,new fjs.PropertyFormatter(null)),
+ new fjs.ArrayFormatter("globalAdmins",false,new fjs.PropertyFormatter(null),undefined,undefined,new fjs.SingleCommentFormatter("Have access to all commands")),
new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Enable/disable text or slash commands."),
new fjs.PropertyFormatter("slashCommands"),
new fjs.PropertyFormatter("textCommands"),
new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Configure the status of the bot."),
new fjs.ObjectFormatter("status",true,[
new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("type"),
- new fjs.PropertyFormatter("mode"),
+ new fjs.PropertyFormatter("type",new fjs.SingleCommentFormatter("Choices: listening, watching, playing, custom")),
+ new fjs.PropertyFormatter("mode",new fjs.SingleCommentFormatter("Choices: online, invisible, idle, dnd")),
new fjs.PropertyFormatter("text"),
- new fjs.PropertyFormatter("state"),
+ new fjs.PropertyFormatter("state",new fjs.SingleCommentFormatter("Additional text (Leave empty to disable)")),
]),
new fjs.TextFormatter(""),
- new fjs.ObjectFormatter("system",true,[
- new fjs.PropertyFormatter("preferSlashOverText"),
- new fjs.PropertyFormatter("sendErrorOnUnknownCommand"),
- new fjs.PropertyFormatter("questionFieldsInCodeBlock"),
- new fjs.PropertyFormatter("displayFieldsWithQuestions"),
- new fjs.PropertyFormatter("showGlobalAdminsInPanelRoles"),
- new fjs.PropertyFormatter("disableVerifyBars"),
- new fjs.PropertyFormatter("useRedErrorEmbeds"),
- new fjs.PropertyFormatter("alwaysShowReason"),
- new fjs.PropertyFormatter("emojiStyle"),
- new fjs.PropertyFormatter("pinEmoji"),
- new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("replyOnTicketCreation"),
- new fjs.PropertyFormatter("replyOnReactionRole"),
- new fjs.PropertyFormatter("askPriorityOnTicketCreation"),
- new fjs.PropertyFormatter("removeParticipantsOnClose"),
- new fjs.PropertyFormatter("disableAutocloseAfterReopen"),
- new fjs.PropertyFormatter("autodeleteRequiresClosedTicket"),
- new fjs.PropertyFormatter("adminOnlyDeleteWithoutTranscript"),
- new fjs.PropertyFormatter("allowCloseBeforeMessage"),
- new fjs.PropertyFormatter("allowCloseBeforeAdminMessage"),
- new fjs.PropertyFormatter("useTranslatedConfigChecker"),
- new fjs.PropertyFormatter("pinFirstTicketMessage"),
- new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("enableTicketClaimButtons"),
- new fjs.PropertyFormatter("enableTicketCloseButtons"),
- new fjs.PropertyFormatter("enableTicketPinButtons"),
- new fjs.PropertyFormatter("enableTicketDeleteButtons"),
- new fjs.PropertyFormatter("enableTicketActionWithReason"),
- new fjs.PropertyFormatter("enableDeleteWithoutTranscript"),
- new fjs.TextFormatter(""),
- new fjs.ObjectFormatter("logs",true,[
- new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("channel"),
- ]),
- new fjs.TextFormatter(""),
- new fjs.ObjectFormatter("limits",true,[
- new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("globalMaximum"),
- new fjs.PropertyFormatter("userMaximum"),
- ]),
- new fjs.TextFormatter(""),
- new fjs.ObjectFormatter("channelTopic",true,[
- new fjs.PropertyFormatter("showOptionName"),
- new fjs.PropertyFormatter("showOptionDescription"),
- new fjs.PropertyFormatter("showOptionTopic"),
- new fjs.PropertyFormatter("showPriority"),
- new fjs.PropertyFormatter("showClosed"),
- new fjs.PropertyFormatter("showClaimed"),
- new fjs.PropertyFormatter("showPinned"),
- new fjs.PropertyFormatter("showCreator"),
- new fjs.PropertyFormatter("showParticipants"),
- ]),
- new fjs.TextFormatter(""),
- new fjs.ObjectFormatter("permissions",true,[
- new fjs.PropertyFormatter("help"),
- new fjs.PropertyFormatter("panel"),
- new fjs.PropertyFormatter("ticket"),
- new fjs.PropertyFormatter("close"),
- new fjs.PropertyFormatter("delete"),
- new fjs.PropertyFormatter("reopen"),
- new fjs.PropertyFormatter("claim"),
- new fjs.PropertyFormatter("unclaim"),
- new fjs.PropertyFormatter("pin"),
- new fjs.PropertyFormatter("unpin"),
- new fjs.PropertyFormatter("move"),
- new fjs.PropertyFormatter("rename"),
- new fjs.PropertyFormatter("add"),
- new fjs.PropertyFormatter("remove"),
- new fjs.PropertyFormatter("blacklist"),
- new fjs.PropertyFormatter("stats"),
- new fjs.PropertyFormatter("clear"),
- new fjs.PropertyFormatter("autoclose"),
- new fjs.PropertyFormatter("autodelete"),
- new fjs.PropertyFormatter("transfer"),
- new fjs.PropertyFormatter("topic"),
- new fjs.PropertyFormatter("priority"),
- ]),
- new fjs.TextFormatter(""),
- new fjs.ObjectFormatter("messages",true,[
+ new fjs.MultiCommentFormatter("Send ticket logs to a channel or in DM of the ticket creator."),
+ new fjs.ObjectFormatter("logs",true,[
+ new fjs.PropertyFormatter("enabled"),
+ new fjs.PropertyFormatter("channel"),
+ new fjs.ObjectFormatter("logMessages",true,[
new fjs.DefaultFormatter("creation",false),
new fjs.DefaultFormatter("closing",false),
new fjs.DefaultFormatter("deleting",false),
@@ -137,80 +79,267 @@ export const defaultGeneralFormatter = new fjs.ObjectFormatter(null,true,[
new fjs.DefaultFormatter("reactionRole",false)
]),
]),
-])
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("A large collection of settings for the ticket system."),
+ new fjs.ObjectFormatter("ticketSystem",true,[
+ new fjs.PropertyFormatter("preferSlashOverText",new fjs.SingleCommentFormatter("Show slashcmds in help menu's")),
+ new fjs.PropertyFormatter("sendErrorOnUnknownCommand",new fjs.SingleCommentFormatter("Send error when command not found")),
+ new fjs.PropertyFormatter("questionFieldsInCodeBlock",new fjs.SingleCommentFormatter("Put question answers in code blocks")),
+ new fjs.PropertyFormatter("displayFieldsWithQuestions",new fjs.SingleCommentFormatter("Display embed fields together with question answers")),
+ new fjs.PropertyFormatter("showGlobalAdminsInPanelRoles",new fjs.SingleCommentFormatter("Include \"globalAdmins\" in panel admin lists")),
+ new fjs.PropertyFormatter("disableVerifyBars",new fjs.SingleCommentFormatter("Disable the (โ/โ
) buttons")),
+ new fjs.PropertyFormatter("useRedErrorEmbeds",new fjs.SingleCommentFormatter("Make errors embeds always red")),
+ new fjs.PropertyFormatter("alwaysShowReason",new fjs.SingleCommentFormatter("Show reason even if none is provided")),
+ new fjs.PropertyFormatter("emojiStyle",new fjs.SingleCommentFormatter("The style of emoji's in embeds. Choices: before, after, double, disabled")),
+ new fjs.PropertyFormatter("pinEmoji",new fjs.SingleCommentFormatter("Channel emoji of pinned tickets (Leave empty to disable)")),
+ new fjs.PropertyFormatter("closeEmoji",new fjs.SingleCommentFormatter("Channel emoji of closed tickets (Leave empty to disable)")),
+ new fjs.TextFormatter(""),
+ new fjs.PropertyFormatter("replyOnTicketCreation",new fjs.SingleCommentFormatter("Reply with a msg when a ticket is created")),
+ new fjs.PropertyFormatter("replyOnReactionRole",new fjs.SingleCommentFormatter("Reply with a msg when a reaction role is used")),
+ new fjs.PropertyFormatter("askPriorityOnTicketCreation",new fjs.SingleCommentFormatter("Show a dropdown to select priority")),
+ new fjs.PropertyFormatter("removeParticipantsOnClose",new fjs.SingleCommentFormatter("Remove non-admins when ticket is closed")),
+ new fjs.PropertyFormatter("disableAutocloseAfterReopen",new fjs.SingleCommentFormatter("Disable autoclose after ticket got reopened")),
+ new fjs.PropertyFormatter("autodeleteRequiresClosedTicket",new fjs.SingleCommentFormatter("A ticket must be closed before autodelete works")),
+ new fjs.PropertyFormatter("adminOnlyDeleteWithoutTranscript",new fjs.SingleCommentFormatter("Only allow \"globalAdmins\" to delete a ticket without transcript")),
+ new fjs.PropertyFormatter("allowCloseBeforeMessage",new fjs.SingleCommentFormatter("Allow closing before a message is sent")),
+ new fjs.PropertyFormatter("allowCloseBeforeAdminMessage",new fjs.SingleCommentFormatter("Allow closing before an admin has sent a message")),
+ new fjs.PropertyFormatter("useTranslatedConfigChecker",new fjs.SingleCommentFormatter("Translate config errors in the console")),
+ new fjs.PropertyFormatter("pinFirstTicketMessage",new fjs.SingleCommentFormatter("Pin the ticket message to the channel")),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Enable/disable certain buttons & features of the bot."),
+ new fjs.PropertyFormatter("enableTicketClaimButtons"),
+ new fjs.PropertyFormatter("enableTicketCloseButtons"),
+ new fjs.PropertyFormatter("enableTicketPinButtons"),
+ new fjs.PropertyFormatter("enableTicketDeleteButtons"),
+ new fjs.PropertyFormatter("enableTicketActionWithReason"),
+ new fjs.PropertyFormatter("enableDeleteWithoutTranscript",new fjs.SingleCommentFormatter("Allow deleting tickets without transcript")),
+ new fjs.PropertyFormatter("enableCreateTicketForOtherUser",new fjs.SingleCommentFormatter("Allow creating tickets for other users")),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Set the maximum amount of simultaneous tickets."),
+ new fjs.ObjectFormatter("limits",true,[
+ new fjs.PropertyFormatter("enabled"),
+ new fjs.PropertyFormatter("globalMaximum"),
+ new fjs.PropertyFormatter("userMaximum"),
+ ]),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Choose which data is shown in the channel topic."),
+ new fjs.ObjectFormatter("channelTopic",true,[
+ new fjs.PropertyFormatter("showOptionName"),
+ new fjs.PropertyFormatter("showOptionDescription"),
+ new fjs.PropertyFormatter("showOptionTopic"),
+ new fjs.PropertyFormatter("showPriority"),
+ new fjs.PropertyFormatter("showClosed"),
+ new fjs.PropertyFormatter("showClaimed"),
+ new fjs.PropertyFormatter("showPinned"),
+ new fjs.PropertyFormatter("showCreator"),
+ new fjs.PropertyFormatter("showParticipants"),
+ ]),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Move closed tickets to a separate category."),
+ new fjs.ObjectFormatter("closedCategory",true,[
+ new fjs.PropertyFormatter("enabled"),
+ new fjs.PropertyFormatter("categoryId")
+ ]),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Create tickets in a backup category when the original category exceeds 50 channels."),
+ new fjs.ObjectFormatter("backupCategory",true,[
+ new fjs.PropertyFormatter("enabled"),
+ new fjs.PropertyFormatter("categoryId")
+ ]),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Move claimed tickets to a matching category of the user that claimed the ticket. Set to empty list [] to disable."),
+ new fjs.ArrayFormatter("claimedCategories",true,new fjs.ObjectFormatter(null,false,[
+ new fjs.PropertyFormatter("user"),
+ new fjs.PropertyFormatter("category"),
+ ])),
+ ]),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter([
+ "Set permissions for each individual command, button or action.",
+ "CHOICES:",
+ ">> \"none\" -> Command disabled",
+ ">> \"everyone\" -> Allowed for everyone",
+ ">> \"admin\" -> Global & ticket admins only",
+ ">> \"DISCORD_ROLE_ID\" -> Custom role only",
+ ].join("\n")),
+ new fjs.ObjectFormatter("permissions",true,[
+ new fjs.PropertyFormatter("help"),
+ new fjs.PropertyFormatter("panel"),
+ new fjs.PropertyFormatter("ticket"),
+ new fjs.PropertyFormatter("close"),
+ new fjs.PropertyFormatter("delete"),
+ new fjs.PropertyFormatter("reopen"),
+ new fjs.PropertyFormatter("claim"),
+ new fjs.PropertyFormatter("unclaim"),
+ new fjs.PropertyFormatter("pin"),
+ new fjs.PropertyFormatter("unpin"),
+ new fjs.PropertyFormatter("move"),
+ new fjs.PropertyFormatter("rename"),
+ new fjs.PropertyFormatter("add"),
+ new fjs.PropertyFormatter("remove"),
+ new fjs.PropertyFormatter("blacklist"),
+ new fjs.PropertyFormatter("stats"),
+ new fjs.PropertyFormatter("clear"),
+ new fjs.PropertyFormatter("autoclose"),
+ new fjs.PropertyFormatter("autodelete"),
+ new fjs.PropertyFormatter("transfer"),
+ new fjs.PropertyFormatter("topic"),
+ new fjs.PropertyFormatter("priority"),
+ new fjs.PropertyFormatter("transcripts"),
+ ]),
+]))
-export const defaultQuestionsFormatter = new fjs.ArrayFormatter(null,true,new fjs.ObjectSwitchFormatter(null,[
+export const defaultQuestionsFormatter = new fjs.TopLevelCommentFormatter(new fjs.MultiCommentFormatter([
+ "OPEN TICKET MODAL QUESTIONS",
+ "----------------------------------------------",
+ "Create customizable modal questions that will be shown before creating a ticket.",
+ "Each ticket option (config/options.jsonc) can contain a maximum of 5 questions.",
+ "There are 6 types of questions available: short, paragraph, dropdown, radio-select, checkbox-select, text-display",
+ "",
+ "TIP: Create new questions by copying everything between and including the {...} brackets of a question. Paste it after the last question and make sure that they are seperated by a comma."
+ ].join("\n")),new fjs.ArrayFormatter(null,true,new fjs.ObjectSwitchFormatter(null,[
{key:"type",value:"short",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A short text input modal question."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.PropertyFormatter("type"),
- new fjs.TextFormatter(""),
new fjs.PropertyFormatter("required"),
+ new fjs.TextFormatter(""),
new fjs.PropertyFormatter("placeholder"),
new fjs.ObjectFormatter("length",true,[
+ new fjs.MultiCommentFormatter("Configure length limits for the answer."),
new fjs.PropertyFormatter("enabled"),
new fjs.PropertyFormatter("min"),
new fjs.PropertyFormatter("max"),
]),
])},
{key:"type",value:"paragraph",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A paragraph text input modal question."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.PropertyFormatter("type"),
- new fjs.TextFormatter(""),
new fjs.PropertyFormatter("required"),
+ new fjs.TextFormatter(""),
new fjs.PropertyFormatter("placeholder"),
new fjs.ObjectFormatter("length",true,[
+ new fjs.MultiCommentFormatter("Configure length limits for the answer."),
new fjs.PropertyFormatter("enabled"),
new fjs.PropertyFormatter("min"),
new fjs.PropertyFormatter("max"),
]),
- ])}
-]))
-
-
-export const defaultOptionsFormatter = new fjs.ArrayFormatter(null,true,new fjs.ObjectSwitchFormatter(null,[
- {key:"type",value:"ticket",formatter:new fjs.ObjectFormatter(null,true,[
+ ])},
+ {key:"type",value:"text-display",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("Show text in a modal to provide extra details or explain questions."),
+ new fjs.PropertyFormatter("id"),
+ new fjs.PropertyFormatter("type"),
+ new fjs.TextFormatter(""),
+ new fjs.PropertyFormatter("textContents"),
+ ])},
+ {key:"type",value:"dropdown",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A dropdown menu input modal question with up to 25 choices. \"emoji\" & \"description\" fields are optional."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
- new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("type"),
+ new fjs.PropertyFormatter("required"),
+ new fjs.TextFormatter(""),
+ new fjs.PropertyFormatter("placeholder"),
+ new fjs.ArrayFormatter("choices",true,new fjs.ObjectFormatter(null,false,[
+ new fjs.PropertyFormatter("title"),
+ new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("emoji"),
+ ])),
+ ])},
+ {key:"type",value:"radio-select",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A radio select input modal question with up to 10 choices."),
+ new fjs.PropertyFormatter("id"),
+ new fjs.PropertyFormatter("name"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("type"),
+ new fjs.PropertyFormatter("required"),
+ new fjs.TextFormatter(""),
+ new fjs.ArrayFormatter("choices",true,new fjs.ObjectFormatter(null,false,[
+ new fjs.PropertyFormatter("title"),
+ new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("selectedByDefault"),
+ ])),
+ ])},
+ {key:"type",value:"checkbox-select",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A checkbox select input modal question with up to 10 choices."),
+ new fjs.PropertyFormatter("id"),
+ new fjs.PropertyFormatter("name"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("type"),
+ new fjs.PropertyFormatter("required"),
+ new fjs.TextFormatter(""),
+ new fjs.ObjectFormatter("limits",true,[
+ new fjs.MultiCommentFormatter("Configure checkbox amount limits for the answer."),
+ new fjs.PropertyFormatter("enabled"),
+ new fjs.PropertyFormatter("min"),
+ new fjs.PropertyFormatter("max"),
+ ]),
+ new fjs.ArrayFormatter("choices",true,new fjs.ObjectFormatter(null,false,[
+ new fjs.PropertyFormatter("title"),
+ new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("selectedByDefault"),
+ ])),
+ ])},
+])))
+
+
+export const defaultOptionsFormatter = new fjs.TopLevelCommentFormatter(new fjs.MultiCommentFormatter([
+ "OPEN TICKET BUTTON OPTIONS",
+ "----------------------------------------------",
+ "Create customizable ticket, website, reaction-role or sub-panel button options.",
+ "Up to 25 options can be added to each panel in (config/panels.jsonc)",
+ "There are 4 types of options available: ticket, website, role, sub-panel",
+ "",
+ "TIP: Create new options by copying everything between and including the {...} brackets of an option. Paste it after the last option and make sure that they are seperated by a comma."
+ ].join("\n")),new fjs.ArrayFormatter(null,true,new fjs.ObjectSwitchFormatter(null,[
+ {key:"type",value:"ticket",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A ticket option creates a button to open a ticket."),
+ new fjs.PropertyFormatter("id"),
+ new fjs.PropertyFormatter("name"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.PropertyFormatter("type"),
new fjs.TextFormatter(""),
new fjs.ObjectFormatter("button",true,[
+ new fjs.MultiCommentFormatter("Configure the button style of this option. At least one of \"emoji\" or \"label\" must be provided."),
new fjs.PropertyFormatter("emoji"),
new fjs.PropertyFormatter("label"),
- new fjs.PropertyFormatter("color"),
+ new fjs.PropertyFormatter("color",new fjs.SingleCommentFormatter("Choices: gray, red, green, blue")),
]),
new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Add up to 5 modal questions IDs from (config/questions.jsonc)."),
+ new fjs.ArrayFormatter("questions",false,new fjs.PropertyFormatter(null)),
+ new fjs.TextFormatter(""),
new fjs.ArrayFormatter("ticketAdmins",false,new fjs.PropertyFormatter(null)),
new fjs.ArrayFormatter("readonlyAdmins",false,new fjs.PropertyFormatter(null)),
new fjs.PropertyFormatter("allowCreationByBlacklistedUsers"),
- new fjs.ArrayFormatter("questions",false,new fjs.PropertyFormatter(null)),
new fjs.TextFormatter(""),
new fjs.ObjectFormatter("channel",true,[
+ new fjs.MultiCommentFormatter("Configure the name, topic and category of the ticket option."),
new fjs.PropertyFormatter("prefix"),
- new fjs.PropertyFormatter("suffix"),
- new fjs.PropertyFormatter("category"),
- new fjs.PropertyFormatter("backupCategory"),
- new fjs.PropertyFormatter("closedCategory"),
- new fjs.ArrayFormatter("claimedCategory",true,new fjs.ObjectFormatter(null,false,[
- new fjs.PropertyFormatter("user"),
- new fjs.PropertyFormatter("category"),
- ])),
- new fjs.PropertyFormatter("topic"),
+ new fjs.PropertyFormatter("suffix",new fjs.SingleCommentFormatter("Choices: user-name, user-id, random-number, random-hex, counter-dynamic, counter-fixed")),
+ new fjs.PropertyFormatter("category",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("topic",new fjs.SingleCommentFormatter("Leave empty to disable")),
]),
new fjs.TextFormatter(""),
new fjs.ObjectFormatter("dmMessage",true,[
+ new fjs.MultiCommentFormatter("Send a customisable message in DM when creating a ticket."),
new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("text"),
+ new fjs.PropertyFormatter("text",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.ObjectFormatter("embed",true,[
new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("title"),
- new fjs.PropertyFormatter("description"),
- new fjs.PropertyFormatter("customColor"),
+ new fjs.PropertyFormatter("title",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("customColor",new fjs.SingleCommentFormatter("Leave empty to use default color")),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("image"),
- new fjs.PropertyFormatter("thumbnail"),
+ new fjs.PropertyFormatter("image",new fjs.SingleCommentFormatter("Image URL. Leave empty to disable")),
+ new fjs.PropertyFormatter("thumbnail",new fjs.SingleCommentFormatter("Image URL. Leave empty to disable")),
+ new fjs.MultiCommentFormatter("Embed fields. Set to empty list [] to disable."),
new fjs.ArrayFormatter("fields",true,new fjs.ObjectFormatter(null,false,[
new fjs.PropertyFormatter("name"),
new fjs.PropertyFormatter("value"),
@@ -220,16 +349,18 @@ export const defaultOptionsFormatter = new fjs.ArrayFormatter(null,true,new fjs.
]),
]),
new fjs.ObjectFormatter("ticketMessage",true,[
+ new fjs.MultiCommentFormatter("Send a customisable message in the ticket with close, claim, delete, ... buttons."),
new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("text"),
+ new fjs.PropertyFormatter("text",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.ObjectFormatter("embed",true,[
new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("title"),
- new fjs.PropertyFormatter("description"),
- new fjs.PropertyFormatter("customColor"),
+ new fjs.PropertyFormatter("title",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("customColor",new fjs.SingleCommentFormatter("Leave empty to use default color")),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("image"),
- new fjs.PropertyFormatter("thumbnail"),
+ new fjs.PropertyFormatter("image",new fjs.SingleCommentFormatter("Image URL. Leave empty to disable")),
+ new fjs.PropertyFormatter("thumbnail",new fjs.SingleCommentFormatter("Image URL. Leave empty to disable")),
+ new fjs.MultiCommentFormatter("Embed fields. Set to empty list [] to disable."),
new fjs.ArrayFormatter("fields",true,new fjs.ObjectFormatter(null,false,[
new fjs.PropertyFormatter("name"),
new fjs.PropertyFormatter("value"),
@@ -238,44 +369,52 @@ export const defaultOptionsFormatter = new fjs.ArrayFormatter(null,true,new fjs.
new fjs.PropertyFormatter("timestamp"),
]),
new fjs.ObjectFormatter("ping",true,[
+ new fjs.MultiCommentFormatter("Customise the user & role mentions of this ticket message."),
new fjs.PropertyFormatter("@here"),
new fjs.PropertyFormatter("@everyone"),
- new fjs.ArrayFormatter("custom",true,new fjs.PropertyFormatter(null)),
+ new fjs.ArrayFormatter("custom",false,new fjs.PropertyFormatter(null)),
]),
]),
new fjs.ObjectFormatter("autoclose",true,[
+ new fjs.MultiCommentFormatter("Autoclose this ticket after a period of inactivity or when the creator leaves the server."),
new fjs.PropertyFormatter("enableInactiveHours"),
new fjs.PropertyFormatter("inactiveHours"),
new fjs.PropertyFormatter("enableUserLeave"),
new fjs.PropertyFormatter("disableOnClaim"),
]),
new fjs.ObjectFormatter("autodelete",true,[
+ new fjs.MultiCommentFormatter("Autodelete this ticket after a period of inactivity or when the creator leaves the server."),
new fjs.PropertyFormatter("enableInactiveDays"),
new fjs.PropertyFormatter("inactiveDays"),
new fjs.PropertyFormatter("enableUserLeave"),
new fjs.PropertyFormatter("disableOnClaim"),
]),
new fjs.ObjectFormatter("cooldown",true,[
+ new fjs.MultiCommentFormatter("Users must wait a certain period before being able to create another ticket of this type."),
new fjs.PropertyFormatter("enabled"),
new fjs.PropertyFormatter("cooldownMinutes"),
]),
new fjs.ObjectFormatter("limits",true,[
+ new fjs.MultiCommentFormatter("Set the maximum amount of simultaneous tickets of this option."),
new fjs.PropertyFormatter("enabled"),
new fjs.PropertyFormatter("globalMaximum"),
new fjs.PropertyFormatter("userMaximum"),
]),
new fjs.ObjectFormatter("slowMode",true,[
+ new fjs.MultiCommentFormatter("Enable slow-mode in the ticket channel."),
new fjs.PropertyFormatter("enabled"),
new fjs.PropertyFormatter("slowModeSeconds"),
]),
])},
{key:"type",value:"website",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A website option creates a button with a URL to an external website."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
- new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.PropertyFormatter("type"),
new fjs.TextFormatter(""),
new fjs.ObjectFormatter("button",true,[
+ new fjs.MultiCommentFormatter("Configure the button style of this option. At least one of \"emoji\" or \"label\" must be provided."),
new fjs.PropertyFormatter("emoji"),
new fjs.PropertyFormatter("label"),
]),
@@ -283,43 +422,75 @@ export const defaultOptionsFormatter = new fjs.ArrayFormatter(null,true,new fjs.
new fjs.PropertyFormatter("url"),
])},
{key:"type",value:"role",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A reaction-role option creates a button for members to choose roles."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
- new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.PropertyFormatter("type"),
new fjs.TextFormatter(""),
new fjs.ObjectFormatter("button",true,[
+ new fjs.MultiCommentFormatter("Configure the button style of this option. At least one of \"emoji\" or \"label\" must be provided."),
new fjs.PropertyFormatter("emoji"),
new fjs.PropertyFormatter("label"),
- new fjs.PropertyFormatter("color"),
+ new fjs.PropertyFormatter("color",new fjs.SingleCommentFormatter("Choices: gray, red, green, blue")),
]),
new fjs.TextFormatter(""),
new fjs.ArrayFormatter("roles",false,new fjs.PropertyFormatter(null)),
- new fjs.PropertyFormatter("mode"),
- new fjs.ArrayFormatter("removeRolesOnAdd",false,new fjs.PropertyFormatter(null)),
- new fjs.PropertyFormatter("addOnMemberJoin"),
+ new fjs.PropertyFormatter("mode",new fjs.SingleCommentFormatter("What to do with the roles. Choices: add&remove, add, remove")),
+ new fjs.ArrayFormatter("removeRolesOnAdd",false,new fjs.PropertyFormatter(null),undefined,undefined,new fjs.SingleCommentFormatter("Remove these old roles when new roles are added.")),
+ new fjs.PropertyFormatter("addOnMemberJoin",new fjs.SingleCommentFormatter("Add these roles automatically when joining the server.")),
+ ])},
+ {key:"type",value:"sub-panel",formatter:new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A sub-panel option creates a button which sends another panel for additional options."),
+ new fjs.PropertyFormatter("id"),
+ new fjs.PropertyFormatter("name"),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("type"),
+ new fjs.TextFormatter(""),
+ new fjs.ObjectFormatter("button",true,[
+ new fjs.MultiCommentFormatter("Configure the button style of this option. At least one of \"emoji\" or \"label\" must be provided."),
+ new fjs.PropertyFormatter("emoji"),
+ new fjs.PropertyFormatter("label"),
+ new fjs.PropertyFormatter("color",new fjs.SingleCommentFormatter("Choices: gray, red, green, blue")),
+ ]),
+ new fjs.TextFormatter(""),
+ new fjs.PropertyFormatter("subPanelId",new fjs.SingleCommentFormatter("Choose a panel ID from (config/panels.jsonc)")),
])}
-]))
+])))
-export const defaultPanelsFormatter = new fjs.ArrayFormatter(null,true,new fjs.ObjectFormatter(null,true,[
+export const defaultPanelsFormatter = new fjs.TopLevelCommentFormatter(new fjs.MultiCommentFormatter([
+ "OPEN TICKET PANELS",
+ "----------------------------------------------",
+ "Create customizable panel messages with buttons or a dropdown.",
+ "Add up to 25 options to this panel from (config/options.jsonc)",
+ "Panels can be customised with text, images, colors and more.",
+ "",
+ "Spawn the panel in Discord using: /panel ",
+ "",
+ "TIP: Create new panels by copying everything between and including the {...} brackets of an panel. Paste it after the last panel and make sure that they are seperated by a comma."
+ ].join("\n")),new fjs.ArrayFormatter(null,true,new fjs.ObjectFormatter(null,true,[
+ new fjs.MultiCommentFormatter("A panel is creates a message with up to 25 options as buttons or dropdown."),
new fjs.PropertyFormatter("id"),
new fjs.PropertyFormatter("name"),
new fjs.PropertyFormatter("dropdown"),
+ new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Add up to 5 option IDs from (config/options.jsonc)."),
new fjs.ArrayFormatter("options",false,new fjs.PropertyFormatter(null)),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("text"),
+ new fjs.PropertyFormatter("text",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.ObjectFormatter("embed",true,[
new fjs.PropertyFormatter("enabled"),
- new fjs.PropertyFormatter("title"),
- new fjs.PropertyFormatter("description"),
+ new fjs.PropertyFormatter("title",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.PropertyFormatter("description",new fjs.SingleCommentFormatter("Leave empty to disable")),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("customColor"),
- new fjs.PropertyFormatter("url"),
+ new fjs.PropertyFormatter("customColor",new fjs.SingleCommentFormatter("Leave empty to use default color")),
+ new fjs.PropertyFormatter("url",new fjs.SingleCommentFormatter("URL. Leave empty to disable")),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("image"),
- new fjs.PropertyFormatter("thumbnail"),
+ new fjs.PropertyFormatter("image",new fjs.SingleCommentFormatter("Image URL. Leave empty to disable")),
+ new fjs.PropertyFormatter("thumbnail",new fjs.SingleCommentFormatter("Image URL. Leave empty to disable")),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("footer"),
+ new fjs.PropertyFormatter("footer",new fjs.SingleCommentFormatter("Leave empty to disable")),
+ new fjs.MultiCommentFormatter("Embed fields. Set to empty list [] to disable."),
new fjs.ArrayFormatter("fields",true,new fjs.ObjectFormatter(null,false,[
new fjs.PropertyFormatter("name"),
new fjs.PropertyFormatter("value"),
@@ -328,53 +499,70 @@ export const defaultPanelsFormatter = new fjs.ArrayFormatter(null,true,new fjs.O
new fjs.PropertyFormatter("timestamp"),
]),
new fjs.ObjectFormatter("settings",true,[
- new fjs.PropertyFormatter("dropdownPlaceholder"),
+ new fjs.PropertyFormatter("dropdownPlaceholder",new fjs.SingleCommentFormatter("Leave empty to use default.")),
+ new fjs.PropertyFormatter("maximumButtonsPerRow"),
new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Display the maximum amount of tickets per user."),
new fjs.PropertyFormatter("enableMaxTicketsWarningInText"),
new fjs.PropertyFormatter("enableMaxTicketsWarningInEmbed"),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("describeOptionsLayout"),
+ new fjs.MultiCommentFormatter("Automatically generate option descriptions from (config/options.jsonc)."),
+ new fjs.PropertyFormatter("describeOptionsLayout",new fjs.SingleCommentFormatter("Choices: simple, normal, detailed")),
new fjs.PropertyFormatter("describeOptionsCustomTitle"),
new fjs.PropertyFormatter("describeOptionsInText"),
new fjs.PropertyFormatter("describeOptionsInEmbedFields"),
new fjs.PropertyFormatter("describeOptionsInEmbedDescription"),
]),
-]))
+])))
-export const defaultTranscriptsFormatter = new fjs.ObjectFormatter(null,true,[
+export const defaultTranscriptsFormatter = new fjs.TopLevelCommentFormatter(new fjs.MultiCommentFormatter([
+ "OPEN TICKET TRANSCRIPTS",
+ "----------------------------------------------",
+ "Enable transcript creation when tickets are deleted. There are 2 available transcript types: HTML & Text",
+ "",
+ "HTML Transcripts (recommended):",
+ "Generate transcripts as HTML files to view in the browser. No server or domain required. HTML Transcripts use an external service to process and host the transcripts.",
+ "",
+ "Text Transcripts:",
+ "Generate transcripts as simple .txt files with limited details. Processing happens fully local.",
+ ].join("\n")),new fjs.ObjectFormatter(null,true,[
new fjs.ObjectFormatter("general",true,[
new fjs.PropertyFormatter("enabled"),
new fjs.TextFormatter(""),
+ new fjs.MultiCommentFormatter("Choose which users and channel get the generated transcript."),
new fjs.PropertyFormatter("enableChannel"),
new fjs.PropertyFormatter("enableCreatorDM"),
new fjs.PropertyFormatter("enableParticipantDM"),
new fjs.PropertyFormatter("enableActiveAdminDM"),
new fjs.PropertyFormatter("enableEveryAdminDM"),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("channel"),
- new fjs.PropertyFormatter("mode"),
+ new fjs.PropertyFormatter("channel",new fjs.SingleCommentFormatter("Where to send transcripts. Leave empty when disabled.")),
+ new fjs.PropertyFormatter("mode",new fjs.SingleCommentFormatter("The type of transcript to use. Choices: html, text")),
]),
new fjs.ObjectFormatter("embedSettings",true,[
- new fjs.PropertyFormatter("customColor"),
+ new fjs.MultiCommentFormatter("Customise the embed which contains the generated transcript file or URL."),
+ new fjs.PropertyFormatter("customColor",new fjs.SingleCommentFormatter("Leave empty to use default color")),
new fjs.PropertyFormatter("listAllParticipants"),
new fjs.PropertyFormatter("includeTicketStats"),
]),
new fjs.ObjectFormatter("textTranscriptStyle",true,[
- new fjs.PropertyFormatter("layout"),
+ new fjs.MultiCommentFormatter("Customise layout of the text transcripts."),
+ new fjs.PropertyFormatter("layout",new fjs.SingleCommentFormatter("Choices: simple, normal, detailed")),
new fjs.PropertyFormatter("includeStats"),
new fjs.PropertyFormatter("includeIds"),
new fjs.PropertyFormatter("includeEmbeds"),
new fjs.PropertyFormatter("includeFiles"),
new fjs.PropertyFormatter("includeBotMessages"),
new fjs.TextFormatter(""),
- new fjs.PropertyFormatter("fileMode"),
- new fjs.PropertyFormatter("customFileName"),
+ new fjs.PropertyFormatter("fileMode",new fjs.SingleCommentFormatter("How to name the transcript file? Choices: custom, channel-name, channel-id, user-name, user-id")),
+ new fjs.PropertyFormatter("customFileName",new fjs.SingleCommentFormatter("Custom filename without extension")),
]),
new fjs.ObjectFormatter("htmlTranscriptStyle",true,[
+ new fjs.MultiCommentFormatter("Customise layout of the HTML transcripts."),
new fjs.ObjectFormatter("background",true,[
new fjs.PropertyFormatter("enableCustomBackground"),
- new fjs.PropertyFormatter("backgroundColor"),
- new fjs.PropertyFormatter("backgroundImage"),
+ new fjs.PropertyFormatter("backgroundColor",new fjs.SingleCommentFormatter("Leave empty to use Open Ticket color (#f8ba00)")),
+ new fjs.PropertyFormatter("backgroundImage",new fjs.SingleCommentFormatter("Image URL to fill entire background. Leave empty to disable")),
]),
new fjs.ObjectFormatter("header",true,[
new fjs.PropertyFormatter("enableCustomHeader"),
@@ -395,4 +583,4 @@ export const defaultTranscriptsFormatter = new fjs.ObjectFormatter(null,true,[
new fjs.PropertyFormatter("imageUrl"),
]),
]),
-])
\ No newline at end of file
+]))
\ No newline at end of file
diff --git a/src/data/framework/helpMenuLoader.ts b/src/data/framework/helpMenuLoader.ts
index 27dbf4e..ab29a72 100644
--- a/src/data/framework/helpMenuLoader.ts
+++ b/src/data/framework/helpMenuLoader.ts
@@ -20,11 +20,11 @@ export async function loadAllHelpMenuComponents(){
if (!generalConfig) return
const prefix = generalConfig.data.prefix
- const enableDeleteWithoutTranscript = generalConfig.data.system.enableDeleteWithoutTranscript
+ const enableDeleteWithoutTranscript = generalConfig.data.ticketSystem.enableDeleteWithoutTranscript
const allowedCommands: string[] = []
- for (const key in generalConfig.data.system.permissions){
- if (generalConfig.data.system.permissions[key] != "none") allowedCommands.push(key)
+ for (const key in generalConfig.data.permissions){
+ if (generalConfig.data.permissions[key] != "none") allowedCommands.push(key)
}
const general = helpmenu.get("opendiscord:general")
diff --git a/src/data/framework/postLoader.ts b/src/data/framework/postLoader.ts
index c23b881..06cfce4 100644
--- a/src/data/framework/postLoader.ts
+++ b/src/data/framework/postLoader.ts
@@ -7,7 +7,7 @@ export async function loadAllPosts(){
if (!transcriptConfig) return
//LOGS CHANNEL
- if (generalConfig.data.system.logs.enabled) opendiscord.posts.add(new api.ODPost("opendiscord:logs",generalConfig.data.system.logs.channel))
+ if (generalConfig.data.logs.enabled) opendiscord.posts.add(new api.ODPost("opendiscord:logs",generalConfig.data.logs.channel))
//TRANSCRIPTS CHANNEL
if (transcriptConfig.data.general.enabled && transcriptConfig.data.general.enableChannel) opendiscord.posts.add(new api.ODPost("opendiscord:transcripts",transcriptConfig.data.general.channel))
diff --git a/src/data/openticket/optionLoader.ts b/src/data/openticket/optionLoader.ts
index a9277c0..de8639d 100644
--- a/src/data/openticket/optionLoader.ts
+++ b/src/data/openticket/optionLoader.ts
@@ -71,9 +71,6 @@ export const loadTicketOption = (option:api.ODOptionsJsonConfig_TicketOption): a
new api.ODOptionData("opendiscord:channel-prefix",option.channel.prefix),
new api.ODOptionData("opendiscord:channel-suffix",option.channel.suffix),
new api.ODOptionData("opendiscord:channel-category",option.channel.category),
- new api.ODOptionData("opendiscord:channel-category-closed",option.channel.closedCategory),
- new api.ODOptionData("opendiscord:channel-category-backup",option.channel.backupCategory),
- new api.ODOptionData("opendiscord:channel-categories-claimed",option.channel.claimedCategory),
new api.ODOptionData("opendiscord:channel-topic",option.channel.topic),
new api.ODOptionData("opendiscord:dm-message-enabled",option.dmMessage.enabled),
diff --git a/src/data/openticket/panelLoader.ts b/src/data/openticket/panelLoader.ts
index d2abbb2..df9b150 100644
--- a/src/data/openticket/panelLoader.ts
+++ b/src/data/openticket/panelLoader.ts
@@ -93,7 +93,7 @@ export function describePanelOptions(mode:"fields"|"text", panel:api.ODPanel): {
}
if (layout == "detailed"){
const optionAdmins = [...opt.get("opendiscord:admins").value]
- if (generalConfig.data.system.showGlobalAdminsInPanelRoles){
+ if (generalConfig.data.ticketSystem.showGlobalAdminsInPanelRoles){
for (const admin of generalConfig.data.globalAdmins){
if (!optionAdmins.includes(admin)) optionAdmins.push(admin)
}
@@ -147,7 +147,7 @@ export function describePanelOptions(mode:"fields"|"text", panel:api.ODPanel): {
}
if (layout == "detailed"){
const optionAdmins = [...opt.get("opendiscord:admins").value]
- if (generalConfig.data.system.showGlobalAdminsInPanelRoles){
+ if (generalConfig.data.ticketSystem.showGlobalAdminsInPanelRoles){
for (const admin of generalConfig.data.globalAdmins){
if (!optionAdmins.includes(admin)) optionAdmins.push(admin)
}
diff --git a/src/index.ts b/src/index.ts
index 0f8f957..2c61a46 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -20,7 +20,7 @@
INFORMATION:
============
- Open Ticket v4.1.3 - ยฉ DJdj Development
+ Open Ticket v4.2.0 - ยฉ DJdj Development
support us: https://github.com/sponsors/DJj123dj
discord: https://discord.dj-dj.be
@@ -151,7 +151,8 @@ const main = async () => {
if (opendiscord.sharedFuses.getFuse("emojiTitleStyleLoading")){
//set emoji style based on config
- opendiscord.sharedFuses.setFuse("emojiTitleStyle",generalConfig.data.system.emojiStyle)
+ const emojiStyle = (generalConfig.data && generalConfig.data.ticketSystem && generalConfig.data.ticketSystem.emojiStyle) ? generalConfig.data.ticketSystem.emojiStyle : "before"
+ opendiscord.sharedFuses.setFuse("emojiTitleStyle",emojiStyle)
}
//load database
@@ -225,7 +226,7 @@ const main = async () => {
}
//handle data migration (PART 2)
- if (lastVersion) await (await import("./core/startup/manageMigration.js")).loadAllAfterInitVersionMigrations(lastVersion)
+ if (lastVersion) await (await import("./core/startup/manageMigration.js")).loadAfterStartupMigrations(lastVersion)
//load config checker
opendiscord.log("Loading config checker...","system")
@@ -253,7 +254,7 @@ const main = async () => {
if (opendiscord.sharedFuses.getFuse("checkerTranslationLoading")){
await (await import("./data/framework/checkerLoader.js")).loadAllConfigCheckerTranslations()
}
- await opendiscord.events.get("onCheckerTranslationLoad").emit([opendiscord.checkers.translation,((generalConfig && generalConfig.data.system && generalConfig.data.system.useTranslatedConfigChecker) ? generalConfig.data.system.useTranslatedConfigChecker : false),opendiscord.checkers])
+ await opendiscord.events.get("onCheckerTranslationLoad").emit([opendiscord.checkers.translation,((generalConfig && generalConfig.data.ticketSystem && generalConfig.data.ticketSystem.useTranslatedConfigChecker) ? generalConfig.data.ticketSystem.useTranslatedConfigChecker : false),opendiscord.checkers])
await opendiscord.events.get("afterCheckerTranslationsLoaded").emit([opendiscord.checkers.translation,opendiscord.checkers])
//render config checker