Solved merge conflicts

This commit is contained in:
JasperAtSchool
2026-05-20 10:42:17 +02:00
247 changed files with 10788 additions and 25817 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
# Contributing Guidelines
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="500px">
[![discord](https://img.shields.io/badge/discord-support%20server-5865F2.svg?style=flat-square&logo=discord)](https://discord.com/invite/26vT9wt3n3) [![version](https://img.shields.io/badge/version-4.1.3-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.3) [![Sponsor DJj123dj](https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors)](https://github.com/sponsors/DJj123dj)
[![discord](https://img.shields.io/badge/discord-support%20server-5865F2.svg?style=flat-square&logo=discord)](https://discord.com/invite/26vT9wt3n3) [![version](https://img.shields.io/badge/version-4.2.0-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0) [![Sponsor DJj123dj](https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors)](https://github.com/sponsors/DJj123dj)
These are the Contributing Guidelines of Open Ticket!<br>
Here you can find everything you need to know about contributing to Open Ticket.<br>
+8 -11
View File
@@ -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:
+8 -4
View File
@@ -17,7 +17,11 @@ otdebug.txt
*/*/*/.DS_Store
**/.DS_Store
.docs/*
!.docs/createDocs.js
!.docs/mergeTranslations.js
!.docs/typedoc-config.json
.backup/*
.tools/*
!.tools/createDocs.js
!.tools/mergeTranslations.js
!.tools/typedoc-config.json
!.tools/pterodactyl-eggs/
!.tools/docker-compose.yml
!.tools/dockerfile
+2 -2
View File
@@ -1,6 +1,6 @@
# Docker File for Open Ticket v4
# Use the official Node.js 20 image from Docker Hub
FROM node:20-alpine
# Use the official Node.js 22 image from Docker Hub
FROM node:22-alpine
# Set pterodactyl working directory inside the container
WORKDIR /home/container
@@ -2,7 +2,7 @@
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="500px">
[![discord](https://img.shields.io/badge/discord-support%20server-5865F2.svg?style=flat-square&logo=discord)](https://discord.com/invite/26vT9wt3n3)
[![version](https://img.shields.io/badge/version-4.1.3-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.3)
[![version](https://img.shields.io/badge/version-4.2.0-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0)
[![Sponsor DJj123dj](https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors)](https://github.com/sponsors/DJj123dj)
[![Open Ticket supports Pterodactyl Eggs!](https://img.shields.io/badge/pterodactyl-supported-10539F?style=flat-square&logo=pterodactyl)](.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.
@@ -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"
}
]
}
+7 -7
View File
@@ -4,7 +4,7 @@
<sub align="center">Related Projects:</sub><br>
<a href="https://odplugins.dj-dj.be"><img src="https://apis.dj-dj.be/cdn/opendiscord/logo.png" alt="Open Discord" height="55px"></a><br><br>
<a href="https://discord.com/invite/26vT9wt3n3"><img alt="Discord Invite Link" src="https://img.shields.io/badge/discord-support%20server-5865F2.svg?style=flat-square&logo=discord"></img></a>
<a href="https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.3"><img alt="Open Ticket Version" src="https://img.shields.io/badge/version-4.1.3-brightgreen.svg?style=flat-square"></img></a>
<a href="https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0"><img alt="Open Ticket Version" src="https://img.shields.io/badge/version-4.2.0-brightgreen.svg?style=flat-square"></img></a>
<a href="https://otdocs.dj-dj.be"><img alt="Open Ticket Documentation" src="https://img.shields.io/badge/discord.js-v14-CB3837.svg?style=flat-square&logo=npm"></img></a>
<a href="https://github.com/open-discord-bots/open-ticket/blob/main/LICENSE"><img alt="Open Ticket License" src="https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square"></img></a>
<a href="https://otdocs.dj-dj.be"><img alt="Open Ticket Stars" src="https://img.shields.io/github/stars/djj123dj/open-ticket?color=yellow&label=stars&logo=github&style=flat-square"></img></a>
@@ -17,7 +17,7 @@
<p align="center">
Open Ticket is the most <b>advanced and customizable</b> Discord ticket bot available. With <b>350+ configurable settings</b>, you have full control over every aspect of your ticket system!
From <code>HTML transcripts</code> and <code>Advanced Plugins</code> to <code>Claiming & Pinning</code>, <code>Questions & Modals</code>, <code>Detailed Statistics</code>, and much more.<br><br>
The bot is fully translated into <b>36+ languages</b> and has been battle-tested in large Discord servers.<br>
The bot is fully translated into <b>37+ languages</b> and has been battle-tested in large Discord servers.<br>
Need help or want to get involved? Feel free to join our <a href="https://discord.dj-dj.be"><b>Discord server</b></a>.
</p>
@@ -25,7 +25,7 @@ Need help or want to get involved? Feel free to join our <a href="https://discor
<p align="center"><sup>❤️ Love Open Ticket? <a href="https://github.com/sponsors/DJj123dj">Sponsorships</a> help fuel our HTML transcript servers and future features! ❤️</sup></p>
---
> **[-> Navigate to (⏱️ Quick Setup)](#️-quick-start-using-cli)**
> **[-> Navigate to (⏱️ Quick Setup)](#️-quick-start-using-interactive-cli-tool)**
> **[-> Navigate to (📚 Documentation)](https://otdocs.dj-dj.be)**
> **[-> Navigate to (📞 Support Server)](https://discord.dj-dj.be)**
@@ -37,7 +37,7 @@ Need help or want to get involved? Feel free to join our <a href="https://discor
- **📈 Scalable** - Made to handle huge servers and has already been **tested in servers with 100k members.**
- **📄 HTML Transcripts** - The **built-in HTML Transcripts Service** provides beautiful & easy-to-use transcripts.
- **✅ Ticket Status** - Close, reopen, delete, claim, pin, rename or move tickets in your server.
- **🇬🇧 Translation** - Every message has been translated in more than **36 languages** by our community.
- **🇬🇧 Translation** - Every message has been translated in more than **37 languages** by our community.
- **🎨 Customisation** - More than **200+ settings** are related to customisation & advanced features.
- **🖥️ Interactions** - The bot has full support for buttons, dropdowns, slash/text commands & modals.
- **∞ Unlimited Possibilities** - Create an infinite amount of tickets, questions & panels.
@@ -134,14 +134,14 @@ A list of amazing people who have contributed or provided supported for **Open T
</table>
### 💬 Translators
With the amazing support of our translators, we've been able to translate Open Ticket in more than **36 languages**!
With the amazing support of our translators, we've been able to translate Open Ticket in more than **37 languages**!
#### Categories:
- **🟢 Available**
- **🤖 Partially Made Using AI**
- **🟠 Incomplete**
- **🔴 Unavailable/Outdated**
|🔍 |Languages (36) |Maintainer (Github/Discord) |
|🔍 |Languages (37) |Maintainer (Github/Discord) |
|----|---------------------|--------------------------------|
|🟢 |🇬🇧 English |djj123dj |
|🟢 |🇳🇱 Dutch |djj123dj |
@@ -167,6 +167,7 @@ With the amazing support of our translators, we've been able to translate Open T
|🟢 |🇮🇷 Persian |dysashop & zhavis |
|🟢 |🇧🇩 Bengali |HanumeshGupta |
|🟢 |❓ Catalan |guillee3 |
|🟢 |🇨🇳 Traditional Chinese|me.october |
|🤖 |🇪🇪 Estonian |iamnotmega |
|🤖 |🇫🇮 Finnish |iamnotmega |
|🤖 |🇯🇵 Japanese |HanumeshGupta |
@@ -179,7 +180,6 @@ With the amazing support of our translators, we've been able to translate Open T
|🤖 |🇷🇺 Russian |NoOneNook |
|🤖 |🇱🇻 Latvian |NoOneNook |
|🤖 |🇻🇳 Vietnamese |ngocdiep2006 |
|🔴 |🇨🇳 Traditional Chinese|[⭐ Contribute!](.github/CONTRIBUTING.md)|
<!--[⭐ Contribute!](.github/CONTRIBUTING.md) -->
## ⭐️ Star History
-125
View File
@@ -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 your bot token here! (or leave empty when 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":"(additional 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}
}
}
}
+175
View File
@@ -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"
}
}
-126
View File
@@ -1,126 +0,0 @@
[
{
"id":"example-ticket",
"name":"Question",
"description":"Create this ticket if you have a question! (or leave empty)",
"type":"ticket",
"button":{
"emoji":"🎫 (or leave empty)",
"label":"question (or leave empty)",
"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 (or leave empty)",
"closedCategory":"category id (or leave empty)",
"backupCategory":"category id (or leave empty)",
"claimedCategory":[
{"user":"user id","category":"category id"}
],
"topic":"This is the topic of this ticket channel and is visible to everyone! (or leave empty)"
},
"dmMessage":{
"enabled":false,
"text":"Thank you for creating a ticket in our server! (or leave empty)",
"embed":{
"enabled":false,
"title":"Embed Title! (or leave empty)",
"description":"Description (or leave empty)",
"customColor":"#f8ab00 (or leave empty)",
"image":"https://www.example.com/image.png (or leave empty)",
"thumbnail":"https://www.example.com/image.png (or leave empty)",
"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 'Question' ticket in our server!\nOur support team will help you as soon as possible!",
"customColor":"#f8ab00 (or leave empty)",
"image":"https://www.example.com/image.png (or leave empty)",
"thumbnail":"https://www.example.com/image.png (or leave empty)",
"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":"Go to our website.",
"type":"website",
"button":{
"emoji":"😃",
"label":"Visit our website"
},
"url":"https://www.dj-dj.be"
},
{
"id":"example-role",
"name":"Update Ping",
"description":"Click here to get notified on 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
}
]
+165
View File
@@ -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)
}
]
-39
View File
@@ -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 clicking one of the buttons below!",
"customColor":"#f8ab00 (or leave empty)",
"url":"https://openticket.dj-dj.be (or leave empty)",
"image":"https://www.example.com/image.png (or leave empty)",
"thumbnail":"https://www.example.com/image.png (or leave empty)",
"footer":"Open Ticket v4.1.3 (or leave empty)",
"fields":[
{"name":"field name","value":"field value","inline":false}
],
"timestamp":false
},
"settings":{
"dropdownPlaceholder":"Create a ticket!",
"enableMaxTicketsWarningInText":false,
"enableMaxTicketsWarningInEmbed":true,
"describeOptionsLayout":"simple OR normal OR detailed",
"describeOptionsCustomTitle":"",
"describeOptionsInText":false,
"describeOptionsInEmbedFields":true,
"describeOptionsInEmbedDescription":false
}
}
]
+57
View File
@@ -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 <id>
*
* 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
}
}
]
-28
View File
@@ -1,28 +0,0 @@
[
{
"id":"example-question-1",
"name":"Example Question 1",
"type":"short",
"required":true,
"placeholder":"Insert your short answer here!",
"length":{
"enabled":false,
"min":0,
"max":1000
}
},
{
"id":"example-question-2",
"name":"Example Question 2",
"type":"paragraph",
"required":false,
"placeholder":"Insert your long answer here!",
"length":{
"enabled":false,
"min":0,
"max":1000
}
}
]
+102
View File
@@ -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."
}
]
-55
View File
@@ -1,55 +0,0 @@
{
"general":{
"enabled":false,
"enableChannel":false,
"enableCreatorDM":false,
"enableParticipantDM":false,
"enableActiveAdminDM":false,
"enableEveryAdminDM":false,
"channel":"transcript channel id (or leave empty)",
"mode":"html OR text"
},
"embedSettings":{
"customColor":"#f8ab00 (or leave empty)",
"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":"this-is-a-transcript (or leave empty)"
},
"htmlTranscriptStyle":{
"background":{
"enableCustomBackground":false,
"backgroundColor":"#f8ba00 (or leave empty)",
"backgroundImage":"https://www.example.com/image.png (or leave empty)"
},
"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"
}
}
}
+70
View File
@@ -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"
}
}
}
+1
View File
@@ -0,0 +1 @@
[]
+6 -158
View File
@@ -4,7 +4,6 @@ const flags = [
//PTERODACTYL PANEL
//add startup flags here (e.g. "--no-compile") when running via the panel
]
process.argv.push(...flags)
/////////////// STARTUP FLAGS ///////////////
/*
@@ -14,170 +13,19 @@ process.argv.push(...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
Support Us: https://github.com/sponsors/DJj123dj/
*/
///////////////////////////////////////////
////////// COMPILATION + STARTUP //////////
///////////////////////////////////////////
const fs = require("fs")
const ts = require("typescript")
const {createHash,Hash} = require("crypto")
const nodepath = require('path')
const ansis = require("ansis")
/** ## What is this?
* This is a function which compares `./src/` with a hash stored in `./dist/hash.txt`.
* The hash is based on the modified date & file metadata of all files in `./src/`.
*
* If the hash is different, the bot will automatically re-compile.
* This will help you save CPU resources because the bot shouldn't re-compile when nothing has been changed :)
*
* @param {string} dir
* @param {Hash|null} upperHash
*/
function computeSourceHash(dir,upperHash){
const hash = upperHash ? upperHash : createHash("sha256")
const info = fs.readdirSync(dir,{withFileTypes:true})
for (const file of info) {
const fullPath = nodepath.join(dir,file.name)
if (file.isFile() && [".js",".ts",".jsx",".tsx"].some((ext) => file.name.endsWith(ext))){
const statInfo = fs.statSync(fullPath)
//compute hash using file metadata
const fileInfo = `${fullPath}:${statInfo.size}:${statInfo.mtimeMs}`
hash.update(fileInfo)
}else if (file.isDirectory()){
//recursively compute all folders
computeSourceHash(fullPath,hash)
}
}
//return when not being called recursively
if (!upperHash) {
return hash.digest("hex")
}
}
function requiresCompilation(){
//check hashes when not using "--compile-only" flag
if (process.argv.includes("--compile-only")) return true
console.log("OT: Comparing prebuilds with source...")
const sourceHash = computeSourceHash("./src/")
const pluginHash = computeSourceHash("./plugins/")
const hash = sourceHash+":"+pluginHash
if (fs.existsSync("./dist/hash.txt")){
const distHash = fs.readFileSync("./dist/hash.txt").toString()
if (distHash === hash) return false
else return true
}else return true
}
function saveNewCompilationHash(){
const sourceHash = computeSourceHash("./src/")
const pluginHash = computeSourceHash("./plugins/")
const hash = sourceHash+":"+pluginHash
fs.writeFileSync("./dist/hash.txt",hash)
}
if (!process.argv.includes("--no-compile")){
const requiredDependencies = new Set()
if (fs.existsSync("./plugins")){
console.log("OT: Reading plugin.json files...")
for (const pluginDir of fs.readdirSync("./plugins")){
if (pluginDir === ".DS_Store") continue
const pluginPath = nodepath.join("./plugins", pluginDir)
if (!fs.statSync(pluginPath).isDirectory()) continue
const pluginJsonPath = nodepath.join(pluginPath, "plugin.json")
if (fs.existsSync(pluginJsonPath)){
try{
const pluginData = JSON.parse(fs.readFileSync(pluginJsonPath).toString())
if (pluginData.npmDependencies && Array.isArray(pluginData.npmDependencies)){
pluginData.npmDependencies.forEach((dep) => {
if (typeof dep === "string" && dep.trim()){
requiredDependencies.add(dep.trim())
}
})
}
}catch(err){
// skip invalid plugin.json files, will be caught later
}
}
}
if (requiredDependencies.size > 0){
console.log("OT: Checking plugin npm dependencies...")
/**@type {string[]} */
const missingDeps = []
for (const dep of requiredDependencies){
try{
require.resolve(dep)
}catch(err){
missingDeps.push(dep)
}
}
if (missingDeps.length > 0){
console.log(ansis.red("OT: ❌ Fatal Error --> Missing npm dependencies required by plugins:\n\n")+ansis.cyan(missingDeps.map((dep) => " - "+dep).join("\n")+"\n"))
console.log("OT: Please install missing dependencies using the following command:\n> "+ansis.bold.green("npm install " + missingDeps.join(" "))+"\n")
process.exit(1)
}
}
}
if (requiresCompilation()){
console.log("OT: Compilation Required...")
//REMOVE EXISTING BUILDS
console.log("OT: Removing Prebuilds...")
fs.rmSync("./dist",{recursive:true,force:true})
//COMPILE TYPESCRIPT
console.log("OT: Compiling Typescript...")
const configPath = nodepath.resolve('./tsconfig.json')
const configFile = ts.readConfigFile(configPath,ts.sys.readFile)
//check for tsconfig errors
if (configFile.error){
const message = ts.formatDiagnosticsWithColorAndContext([configFile.error],ts.createCompilerHost({}))
console.error(message)
process.exit(1)
}
//parse tsconfig file
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config,ts.sys,nodepath.dirname(configPath))
//create program/compiler
const program = ts.createProgram({
rootNames:parsedConfig.fileNames,
options:parsedConfig.options
})
//emit all compiled files
const emitResult = program.emit()
//print emit errors/warnings (type errors)
const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics)
const formattedDiagnostics = ts.formatDiagnosticsWithColorAndContext(allDiagnostics, ts.createCompilerHost(parsedConfig.options))
console.log(formattedDiagnostics)
if (emitResult.emitSkipped || allDiagnostics.find((d) => d.category == ts.DiagnosticCategory.Error || d.category == ts.DiagnosticCategory.Warning)){
console.log("OT: Compilation Failed!")
process.exit(1)
}
}else console.log("OT: No Compilation Required...")
//save new compilation hash
saveNewCompilationHash()
}
//START BOT
console.log("OT: Compilation Succeeded!")
if (process.argv.includes("--compile-only")) process.exit(0) //exit when only compile is required!
console.log("OT: Starting Bot!")
require("./dist/src/index.js")
import { frameworkStartup } from "@open-discord-bots/framework"
frameworkStartup(flags,"openticket",async () => {
await import("./dist/src/index.js")
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["palestinian"],
"lastedited":"16/02/2026",
"language":"Arabic",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta"],
"lastedited":"16/02/2026",
"language":"Bengali",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["guillee3"],
"lastedited":"16/02/2026",
"language":"Catalan",
+322 -322
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["DJj123dj"],
"lastedited":"16/02/2026",
"language":"Custom",
@@ -13,94 +13,94 @@
"typeWarning":"[WARNING]",
"typeInfo":"[INFO]",
"headerConfigChecker":"CONFIG CHECKER",
"headerDescription":"check for errors in your config files!",
"footerError":"the bot won't start until all {0}'s are fixed!",
"footerWarning":"it's recommended to fix all {0}'s before starting!",
"headerDescription":"Validating config files...",
"footerError":"The bot will not start until all {0}'s are resolved.",
"footerWarning":"The bot may behave unexpectedly until all {0}'s are resolved.",
"footerSupport":"SUPPORT: {0} - DOCS: {1}",
"compactInformation":"use {0} for more information!",
"compactInformation":"Use {0} for a detailed config report.",
"dataPath":"path",
"dataDocs":"docs",
"dataMessages":"message"
},
"messages":{
"stringTooShort":"This string can't be shorter than {0} characters!",
"stringTooLong":"This string can't be longer than {0} characters!",
"stringLengthInvalid":"This string needs to be {0} characters long!",
"stringStartsWith":"This string needs to start with {0}!",
"stringEndsWith":"This string needs to end with {0}!",
"stringContains":"This string needs to contain {0}!",
"stringChoices":"This string can only be one of the following values: {0}!",
"stringRegex":"This string is invalid!",
"stringInvertedContains":"This string is not allowed to contain {0}!",
"stringLowercase":"This string must be written in lowercase only!",
"stringUppercase":"This string must be written in uppercase only!",
"stringSpecialCharacters":"This string is not allowed to contain any special characters! (a-z, 0-9 & space only)",
"stringNoSpaces":"This string is not allowed to contain spaces!",
"stringCapitalWord":"It's recommended that each word in this string starts with a capital letter!",
"stringCapitalSentence":"It looks like some sentences in this string don't start with a capital letter!",
"stringPunctuation":"It looks like the sentence in this string doesn't end with a punctuation mark!",
"stringTooShort":"Text must be at least {0} characters long",
"stringTooLong":"Text must be no longer than {0} characters",
"stringLengthInvalid":"Text must be exactly {0} characters long",
"stringStartsWith":"Text must start with {0}",
"stringEndsWith":"Text must end with {0}",
"stringContains":"Text must contain {0}",
"stringChoices":"Text must be one of the following: {0}",
"stringRegex":"Text does not match the required format",
"stringInvertedContains":"Text must not contain {0}",
"stringLowercase":"Text must be entirely lowercase",
"stringUppercase":"Text must be entirely uppercase",
"stringSpecialCharacters":"Text must only contain letters (a–z), numbers (0–9), and spaces",
"stringNoSpaces":"Text must not contain spaces",
"stringCapitalWord":"Each word in this value should start with a capital letter",
"stringCapitalSentence":"One or more sentences in this value do not start with a capital letter",
"stringPunctuation":"The sentence in this value does not end with a punctuation mark",
"numberTooShort":"This number can't be shorter than {0} characters!",
"numberTooLong":"This number can't be longer than {0} characters!",
"numberLengthInvalid":"This number needs to be {0} characters long!",
"numberTooSmall":"This number needs to be at least {0}!",
"numberTooLarge":"This number needs to be at most {0}!",
"numberNotEqual":"This number needs to be {0}!",
"numberStep":"This number needs to be a multiple of {0}!",
"numberStepOffset":"This number needs to be a multiple of {0} starting with {1}!",
"numberStartsWith":"This number needs to start with {0}!",
"numberEndsWith":"This number needs to end with {0}!",
"numberContains":"This number needs to contain {0}!",
"numberChoices":"This number can only be one of the following values: {0}!",
"numberFloat":"This number can't be a decimal!",
"numberNegative":"This number can't be negative!",
"numberPositive":"This number can't be positive!",
"numberZero":"This number can't be zero!",
"numberNan":"This number can't be NaN (Not A Number)!",
"numberInvertedContains":"This number is not allowed to contain {0}!",
"numberTooShort":"Number must be at least {0} digits long",
"numberTooLong":"Number must be no longer than {0} digits",
"numberLengthInvalid":"Number must be exactly {0} digits long",
"numberTooSmall":"Number must be at least {0}",
"numberTooLarge":"Number must be at most {0}",
"numberNotEqual":"Number must be exactly {0}",
"numberStep":"Number must be a multiple of {0}",
"numberStepOffset":"Number must be a multiple of {0}, starting from {1}",
"numberStartsWith":"Number must start with {0}",
"numberEndsWith":"Number must end with {0}",
"numberContains":"Number must contain {0}",
"numberChoices":"Number must be one of the following: {0}",
"numberFloat":"Number must be a whole number",
"numberNegative":"Number must be a positive number",
"numberPositive":"Number must be a negative number",
"numberZero":"Number must not be zero",
"numberNan":"Number must be a valid number",
"numberInvertedContains":"Number must not contain {0}",
"booleanTrue":"This boolean can't be true!",
"booleanFalse":"This boolean can't be false!",
"booleanTrue":"Boolean must be false",
"booleanFalse":"Boolean must be true",
"arrayEmptyDisabled":"List must not be empty",
"arrayEmptyRequired":"List must be empty",
"arrayTooShort":"List must have at least {0} items",
"arrayTooLong":"List must have at most {0} items",
"arrayLengthInvalid":"List must have exactly {0} items",
"arrayInvalidTypes":"List may only contain the following types: {0}",
"arrayDouble":"List must not contain duplicate values",
"discordInvalidId":"Invalid Discord {0} ID",
"discordInvalidIdOptions":"Invalid Discord {0} ID. Alternatively, use one of the following: {1}",
"discordInvalidToken":"Invalid Discord token",
"colorInvalid":"Invalid hex color",
"emojiTooShort":"Value must contain at least {0} emoji",
"emojiTooLong":"Value must contain at most {0} emoji",
"emojiCustom":"Custom Discord emojis are not allowed here",
"emojiInvalid":"Invalid emoji",
"urlInvalid":"Invalid URL",
"urlInvalidHttp":"URL must use the https:// protocol",
"urlInvalidProtocol":"URL must use the http:// or https:// protocol",
"urlInvalidHostname":"URL hostname is not allowed",
"urlInvalidExtension":"Invalid URL extension. Allowed extensions: {0}",
"urlInvalidPath":"Invalid URL path",
"idNotUnique":"This ID is already in use. Please choose a unique ID",
"idNonExistent":"ID {0} does not exist",
"invalidType":"Property must be of type: {0}",
"propertyMissing":"Required property {0} is missing from the object",
"propertyOptional":"Property {0} is optional in the object",
"objectDisabled":"This object is disabled. Enable it using {0}",
"nullInvalid":"Property must not be null",
"switchInvalidType":"Value must be one of the following types: {0}",
"objectSwitchInvalid":"Object must be one of the following types: {0}",
"arrayEmptyDisabled":"This array isn't allowed to be empty!",
"arrayEmptyRequired":"This array is required to be empty!",
"arrayTooShort":"This array needs to have a length of at least {0}!",
"arrayTooLong":"This array needs to have a length of at most {0}!",
"arrayLengthInvalid":"This array needs to have a length of {0}!",
"arrayInvalidTypes":"This array can only contain the following types: {0}!",
"arrayDouble":"This array doesn't allow the same value twice!",
"discordInvalidId":"This is an invalid discord {0} id!",
"discordInvalidIdOptions":"This is an invalid discord {0} id! You can also use one of these: {1}!",
"discordInvalidToken":"This is an invalid discord token (syntactically)!",
"colorInvalid":"This is an invalid hex color!",
"emojiTooShort":"This string needs to have at least {0} emoji's!",
"emojiTooLong":"This string needs to have at most {0} emoji's!",
"emojiCustom":"This emoji can't be a custom discord emoji!",
"emojiInvalid":"This is an invalid emoji!",
"urlInvalid":"This url is invalid!",
"urlInvalidHttp":"This url can only use the https:// protocol!",
"urlInvalidProtocol":"This url can only use the http:// & https:// protocols!",
"urlInvalidHostname":"This url has a disallowed hostname!",
"urlInvalidExtension":"This url has an invalid extension! Choose between: {0}!",
"urlInvalidPath":"This url has an invalid path!",
"idNotUnique":"This id isn't unique, use another id instead!",
"idNonExistent":"The id {0} doesn't exist!",
"invalidType":"This property needs to be the type: {0}!",
"propertyMissing":"The property {0} is missing from this object!",
"propertyOptional":"The property {0} is optional in this object!",
"objectDisabled":"This object is disabled, enable it using {0}!",
"nullInvalid":"This property can't be null!",
"switchInvalidType":"This needs to be one of the following types: {0}!",
"objectSwitchInvalid":"This object needs to be one of the following types: {0}!",
"invalidLanguage":"This is an invalid language!",
"invalidButton":"This button needs to have at least an {0} or {1}!",
"unusedOption":"The option {0} isn't used anywhere!",
"unusedQuestion":"The question {0} isn't used anywhere!",
"dropdownOption":"A panel with dropdown enabled can only contain options of the 'ticket' type!",
"customInvalidVersion":"The version specified in your config does not match! Make sure you have updated the config to the latest version!"
"invalidLanguage":"Invalid language",
"invalidButton":"Button must have at least an {0} or {1}",
"unusedOption":"Option {0} is not used anywhere",
"unusedQuestion":"Question {0} is not used anywhere",
"dropdownOption":"Panels with dropdown enabled may only contain options of the 'ticket' type",
"customInvalidVersion":"Config version mismatch. Make sure to update your config to the latest version"
}
},
"actions":{
@@ -159,118 +159,118 @@
"transfer":"Ticket Transferred"
},
"descriptions":{
"create":"Your ticket has been created. Click the button below to access it!",
"close":"The ticket has been closed successfully!",
"delete":"The ticket has been deleted successfully!",
"reopen":"The ticket has been reopened successfully!",
"claim":"The ticket has been claimed successfully!",
"unclaim":"The ticket has been unclaimed successfully!",
"pin":"The ticket has been pinned successfully!",
"unpin":"The ticket has been unpinned successfully!",
"rename":"The ticket has been renamed to {0} successfully!",
"move":"The ticket has been moved to {0} successfully!",
"add":"{0} has been added to the ticket successfully!",
"remove":"{0} has been removed from the ticket successfully!",
"create":"Your ticket is ready. Click the button below to view and continue.",
"close":"The ticket has been closed.",
"delete":"The ticket has been deleted.",
"reopen":"The ticket has been reopened.",
"claim":"The ticket has been claimed.",
"unclaim":"The ticket has been unclaimed.",
"pin":"The ticket has been pinned.",
"unpin":"The ticket has been unpinned.",
"rename":"The ticket has been renamed to {0}.",
"move":"The ticket has been moved to {0}.",
"add":"{0} has been added to the ticket.",
"remove":"{0} has been removed from the ticket.",
"helpExplanation":"`<name>` => required parameter\n`[name]` => optional parameter",
"statsReset":"The bot stats have been reset successfully!",
"statsError":"Unable to view ticket stats!\n{0} is not a ticket!",
"blacklistAdd":"{0} has been blacklisted successfully!",
"blacklistRemove":"{0} has been released successfully!",
"blacklistGetSuccess":"{0} is currently blacklisted!",
"blacklistGetEmpty":"{0} is currently not blacklisted!",
"blacklistViewEmpty":"No-one has been blacklisted yet!",
"blacklistViewTip":"Use \"/blacklist add\" to blacklist a user!",
"clearVerify":"Are you sure you want to delete multiple tickets?\nThis action can't be undone!",
"clearReady":"{0} tickets have been deleted successfully!",
"rolesEmpty":"No roles have been updated!",
"statsReset":"The bot statistics have been reset.",
"statsError":"Unable to retrieve ticket statistics.\n{0} is not a valid ticket.",
"blacklistAdd":"{0} has been blacklisted.",
"blacklistRemove":"{0} has been released.",
"blacklistGetSuccess":"{0} is blacklisted!",
"blacklistGetEmpty":"{0} is not blacklisted!",
"blacklistViewEmpty":"No users have been blacklisted yet.",
"blacklistViewTip":"Use \"/blacklist add\" to add a user to the blacklist.",
"clearVerify":"Are you sure you want to delete multiple tickets?\nThis action cannot be undone.",
"clearReady":"{0} ticket(s) have been deleted.",
"rolesEmpty":"No roles were modified.",
"autocloseLeave":"This ticket has been autoclosed because the creator left the server!",
"autocloseTimeout":"This ticket has been autoclosed because it has been inactive for more than `{0}h`!",
"autodeleteLeave":"This ticket has been autodeleted because the creator left the server!",
"autodeleteTimeout":"This ticket has been autodeleted because it has been inactive for more than `{0} days`!",
"autocloseEnabled":"Autoclose has been enabled in this ticket!\nIt will be closed when it is inactive for more than `{0}h`!",
"autocloseDisabled":"Autoclose has been disabled in this ticket!\nIt won't be closed automatically anymore!",
"autodeleteEnabled":"Autodelete has been enabled in this ticket!\nIt will be deleted when it is inactive for more than `{0} days`!",
"autodeleteDisabled":"Autodelete has been disabled in this ticket!\nIt won't be deleted automatically anymore!",
"autocloseLeave":"This ticket was automatically closed because its creator left the server.",
"autocloseTimeout":"This ticket was automatically closed due to inactivity exceeding `{0}h`.",
"autodeleteLeave":"This ticket was automatically deleted because its creator left the server.",
"autodeleteTimeout":"This ticket was automatically deleted due to inactivity exceeding `{0} days`.",
"autocloseEnabled":"Autoclose has been enabled for this ticket.\nIt will close after `{0}h` of inactivity.",
"autocloseDisabled":"Autoclose has been disabled for this ticket.\nThis ticket will no longer close automatically.",
"autodeleteEnabled":"Autodelete has been enabled for this ticket.\nIt will be deleted after `{0} days` of inactivity.",
"autodeleteDisabled":"Autodelete has been disabled for this ticket.\nThis ticket will no longer be deleted automatically.",
"ticketMessageLimit":"You can only create {0} ticket(s) at the same time!",
"ticketMessageAutoclose":"This ticket will be autoclosed when inactive for {0}h!",
"ticketMessageAutodelete":"This ticket will be autodeleted when inactive for {0} days!",
"panelReady":"The panel is available in the followup message!\nThis message can now be deleted!",
"ticketMessageLimit":"You can only have {0} active ticket(s) at a time.",
"ticketMessageAutoclose":"This ticket will automatically close after `{0}h` of inactivity.",
"ticketMessageAutodelete":"This ticket will automatically be deleted after `{0} days` of inactivity.",
"panelReady":"The panel has been sent in the follow-up message.\nYou may now delete this message.",
"topicSet":"The channel topic has been changed by {0} successfully!",
"prioritySet":"The ticket priority has been changed to {0} by {1} successfully!",
"priorityGet":"The current priority of this ticket is {0}.",
"transfer":"The ticket ownership has been transferred from {0} to {1} by {2} successfully!"
"topicSet":"The channel topic has been changed by {0}.",
"prioritySet":"The ticket priority has been changed to {0} by {1}.",
"priorityGet":"The priority of this ticket is {0}.",
"transfer":"The ticket ownership has been transferred from {0} to {1} by {2}."
},
"modal":{
"closePlaceholder":"Why did you close this ticket?",
"deletePlaceholder":"Why did you delete this ticket?",
"reopenPlaceholder":"Why did you reopen this ticket?",
"claimPlaceholder":"Why did you claim this ticket?",
"unclaimPlaceholder":"Why did you unclaim this ticket?",
"pinPlaceholder":"Why did you pin this ticket?",
"unpinPlaceholder":"Why did you unpin this ticket?"
"closePlaceholder":"Why would you like to close this ticket?",
"deletePlaceholder":"Why would you like to delete this ticket?",
"reopenPlaceholder":"Why would you like to reopen this ticket?",
"claimPlaceholder":"Why would you like to claim this ticket?",
"unclaimPlaceholder":"Why would you like to unclaim this ticket?",
"pinPlaceholder":"Why would you like to pin this ticket?",
"unpinPlaceholder":"Why would you like to unpin this ticket?"
},
"logs":{
"createLog":"A new ticket got created by {0}!",
"closeLog":"This ticket has been closed by {0}!",
"closeDm":"Your ticket has been closed in our server!",
"deleteLog":"This ticket has been deleted by {0}!",
"deleteDm":"Your ticket has been deleted in our server!",
"reopenLog":"This ticket has been reopened by {0}!",
"reopenDm":"Your ticket has been reopened in our server!",
"claimLog":"This ticket has been claimed by {0}!",
"claimDm":"Your ticket has been claimed in our server!",
"unclaimLog":"This ticket has been unclaimed by {0}!",
"unclaimDm":"Your ticket has been unclaimed in our server!",
"pinLog":"This ticket has been pinned by {0}!",
"pinDm":"Your ticket has been pinned in our server!",
"unpinLog":"This ticket has been unpinned by {0}!",
"unpinDm":"Your ticket has been unpinned in our server!",
"renameLog":"This ticket has been renamed to {0} by {1}!",
"renameDm":"Your ticket has been renamed to {0} in our server!",
"moveLog":"This ticket has been moved to {0} by {1}!",
"moveDm":"Your ticket has been moved to {0} in our server!",
"addLog":"{0} has been added to this ticket by {1}!",
"addDm":"{0} has been added to your ticket in our server!",
"removeLog":"{0} has been removed from this ticket by {1}!",
"removeDm":"{0} has been removed from your ticket in our server!",
"createLog":"A new ticket got created by {0}.",
"closeLog":"This ticket has been closed by {0}.",
"closeDm":"Your ticket has been closed.",
"deleteLog":"This ticket has been deleted by {0}.",
"deleteDm":"Your ticket has been deleted.",
"reopenLog":"This ticket has been reopened by {0}.",
"reopenDm":"Your ticket has been reopened.",
"claimLog":"This ticket has been claimed by {0}.",
"claimDm":"Your ticket has been claimed.",
"unclaimLog":"This ticket has been unclaimed by {0}.",
"unclaimDm":"Your ticket has been unclaimed.",
"pinLog":"This ticket has been pinned by {0}.",
"pinDm":"Your ticket has been pinned.",
"unpinLog":"This ticket has been unpinned by {0}.",
"unpinDm":"Your ticket has been unpinned.",
"renameLog":"This ticket has been renamed to {0} by {1}.",
"renameDm":"Your ticket has been renamed to {0}.",
"moveLog":"This ticket has been moved to {0} by {1}.",
"moveDm":"Your ticket has been moved to {0}.",
"addLog":"{0} has been added to this ticket by {1}.",
"addDm":"{0} has been added to your ticket.",
"removeLog":"{0} has been removed from this ticket by {1}.",
"removeDm":"{0} has been removed from your ticket.",
"blacklistAddLog":"{0} was blacklisted by {1}!",
"blacklistRemoveLog":"{0} was removed from the blacklist by {1}!",
"blacklistAddDm":"You have been blacklisted in our server!\nFrom now on, you are unable to create a ticket!",
"blacklistRemoveDm":"You have been removed from the blacklist in our server!\nNow you can create tickets again!",
"clearLog":"{0} tickets have been deleted by {1}!",
"blacklistAddLog":"{0} has been blacklisted by {1}.",
"blacklistRemoveLog":"{0} has been removed from the blacklist by {1}.",
"blacklistAddDm":"You have been blacklisted from this server.\nYou are no longer able to create tickets.",
"blacklistRemoveDm":"You have been removed from the blacklist.\nYou can now create tickets again.",
"clearLog":"{0} ticket(s) have been deleted by {1}.",
"transferLog":"The ownership of this ticket has been transferred from {0} to {1} by {2}!",
"transferDm":"The ownership of your ticket has been transferred from {0} to {1} in our server!",
"prioritySetLog":"The priority of this ticket has been changed to {0} by {1}!",
"prioritySetDm":"The priority of your ticket has been changed to {0} in our server!",
"roleUpdateLog":"{0} has updated their roles!",
"roleUpdateDm":"Your roles in our server have been updated!"
"transferLog":"Ticket ownership has been transferred from {0} to {1} by {2}.",
"transferDm":"Ownership of your ticket has been transferred from {0} to {1}.",
"prioritySetLog":"The priority of this ticket has been set to {0} by {1}.",
"prioritySetDm":"The priority of your ticket has been set to {0}.",
"roleUpdateLog":"{0} has modified their roles.",
"roleUpdateDm":"Your roles have been modified."
}
},
"transcripts":{
"success":{
"visit":"Visit Transcript",
"ready":"Transcript Created",
"textFileDescription":"This is the text transcript of a deleted ticket!",
"htmlProgress":"Please wait while this html transcript is getting processed...",
"createdChannel":"A new {0} transcript has been created in the server!",
"createdCreator":"A new {0} transcript has been created for one of your tickets!",
"createdParticipant":"A new {0} transcript has been created in one of the tickets you participated in!",
"createdActiveAdmin":"A new {0} transcript has been created in one of the tickets you participated as admin!",
"createdEveryAdmin":"A new {0} transcript has been created in one of the tickets you were admin in!",
"createdOther":"A new {0} transcript has been created!"
"textFileDescription":"This is a text transcript of a deleted ticket.",
"htmlProgress":"Please wait while the HTML transcript is being generated...",
"createdChannel":"A new {0} transcript has been created in the server.",
"createdCreator":"A new {0} transcript has been created for one of your tickets.",
"createdParticipant":"A new {0} transcript has been created for a ticket you participated in.",
"createdActiveAdmin":"A new {0} transcript has been created for a ticket you participated in as an admin.",
"createdEveryAdmin":"A new {0} transcript has been created for a ticket you managed as an admin.",
"createdOther":"A new {0} transcript has been created."
},
"errors":{
"retry":"Retry",
"continue":"Delete Without Transcript",
"backup":"Create Backup Transcript",
"error":"Something went wrong while trying to create the transcript.\nWhat would you like to do?\n\nThis ticket won't be deleted until you click one of these buttons.",
"error":"Something went wrong while creating the transcript.\nWhat would you like to do?\n\nThis ticket will not be deleted until you select one of the options below.",
"title":"Transcript Error"
},
"text":{
@@ -279,7 +279,7 @@
"fileTitle":"FILE",
"fieldsTitle":"FIELDS",
"reactionsTitle":"REACTIONS",
"statsTitle":"STATS",
"statsTitle":"STATISTICS",
"emptyContent":"<content is empty>",
"noTitle":"<no-title>",
"noDesc":"<no-description>"
@@ -302,68 +302,68 @@
"permissionError":"Permission Error"
},
"descriptions":{
"askForInfo":"Contact the owner of this bot for more info!",
"askForInfoResolve":"Contact the bot owner of this bot if this issue doesn't resolve after a few tries.",
"internalError":"Failed to respond to this {0} due to an internal error!",
"optionMissing":"A required parameter is missing in this command!",
"optionInvalid":"A parameter in this command is invalid!",
"optionInvalidChoose":"Choose between",
"unknownCommand":"Try visiting the help menu for more info!",
"noPermissions":"You are not allowed to use this {0}!",
"noPermissionsList":"Required Permissions: (one of them)",
"noPermissionsCooldown":"You are not allowed to use this {0} because you have a cooldown!",
"noPermissionsBlacklist":"You are not allowed to use this {0} because you have been blacklisted!",
"noPermissionsLimitGlobal":"You are not allowed to create a ticket because the server reached the max tickets limit!",
"noPermissionsLimitGlobalUser":"You are not allowed to create a ticket because you reached the max tickets limit!",
"noPermissionsLimitOption":"You are not allowed to create a ticket because the server reached the max tickets limit for this option!",
"noPermissionsLimitOptionUser":"You are not allowed to create a ticket because you reached the max tickets limit for this option!",
"unknownTicket":"Try this command again in a valid ticket!",
"deprecatedTicket":"The current channel is not a valid ticket! It might have been a ticket from an old Open Ticket version!",
"notInGuild":"This {0} doesn't work in DM! Please try it again in a server!",
"channelRename":"Due to discord ratelimits, it's currently impossible for the bot to rename the channel. The channel will automatically be renamed over 10 minutes if the bot isn't rebooted.",
"channelRenameSource":"The source of this error is: {0}",
"busy":"Unable to use this {0}!\nThe ticket is currently being processed by the bot.\n\nPlease try again in a few seconds!",
"closeBeforeMessage":"This ticket cannot be closed/deleted before a message has been sent by a user.",
"closeBeforeAdminMessage":"This ticket cannot be closed/deleted before a message has been sent by a ticket admin or support member.",
"unableToCreateTicket":"You are unable to create a ticket."
"askForInfo":"Please contact the bot owner for more information.",
"askForInfoResolve":"If the issue persists after a few attempts, please contact the bot owner.",
"internalError":"An internal error occurred while processing this {0}.",
"optionMissing":"A required parameter is missing for this command.",
"optionInvalid":"One or more parameters provided for this command are invalid.",
"optionInvalidChoose":"Please choose between",
"unknownCommand":"Please use the help menu for more information.",
"noPermissions":"You are not permitted to use this {0}.",
"noPermissionsList":"Required permissions (one of the following):",
"noPermissionsCooldown":"You cannot use this {0} while on cooldown.",
"noPermissionsBlacklist":"You cannot use this {0} because you are blacklisted.",
"noPermissionsLimitGlobal":"You cannot create a ticket because the server has reached its maximum ticket limit.",
"noPermissionsLimitGlobalUser":"You cannot create a ticket because you have reached your maximum ticket limit.",
"noPermissionsLimitOption":"You cannot create a ticket because the server has reached the maximum ticket limit for this option.",
"noPermissionsLimitOptionUser":"You cannot create a ticket because you have reached the maximum ticket limit for this option.",
"unknownTicket":"Please run this command inside a valid ticket channel.",
"deprecatedTicket":"This channel is not recognized as a valid ticket. It may have been created with an older version of the bot.",
"notInGuild":"This {0} cannot be used in direct messages. Please use it in a server.",
"channelRename":"Due to Discord rate limits, the channel could not be renamed immediately. It will be renamed automatically within 10 minutes if the bot remains online.",
"channelRenameSource":"Error source: {0}",
"busy":"This {0} is currently unavailable.\nThe ticket is being processed by the bot.\n\nPlease try again in a few seconds.",
"closeBeforeMessage":"This ticket cannot be closed or deleted before a user has sent a message.",
"closeBeforeAdminMessage":"This ticket cannot be closed or deleted before a support member or admin has sent a message.",
"unableToCreateTicket":"You are currently unable to create a ticket."
},
"optionInvalidReasons":{
"stringRegex":"Value doesn't match pattern!",
"stringMinLength":"Value needs to be at least {0} characters!",
"stringMaxLength":"Value needs to be at most {0} characters!",
"numberInvalid":"Invalid number!",
"numberMin":"Number needs to be at least {0}!",
"numberMax":"Number needs to be at most {0}!",
"numberDecimal":"Number is not allowed to be a decimal!",
"numberNegative":"Number is not allowed to be negative!",
"numberPositive":"Number is not allowed to be positive!",
"numberZero":"Number is not allowed to be zero!",
"channelNotFound":"Unable to find channel!",
"userNotFound":"Unable to find user!",
"roleNotFound":"Unable to find role!",
"memberNotFound":"Unable to find user!",
"mentionableNotFound":"Unable to find user or role!",
"channelType":"Invalid channel type!",
"notInGuild":"This option requires you to be in a server!"
"stringRegex":"The value does not match the required pattern.",
"stringMinLength":"The value must be at least {0} characters long.",
"stringMaxLength":"The value must be at most {0} characters long.",
"numberInvalid":"Invalid number provided.",
"numberMin":"The number must be at least {0}.",
"numberMax":"The number must be at most {0}.",
"numberDecimal":"Decimals are not allowed.",
"numberNegative":"Negative numbers are not allowed.",
"numberPositive":"Positive numbers are not allowed.",
"numberZero":"Zero is not allowed.",
"channelNotFound":"Channel not found.",
"userNotFound":"User not found.",
"roleNotFound":"Role not found.",
"memberNotFound":"Member not found.",
"mentionableNotFound":"User or role not found.",
"channelType":"Invalid channel type.",
"notInGuild":"This option can only be used in a server."
},
"permissions":{
"developer":"You need to be the developer of the bot.",
"owner":"You need to be the server owner.",
"admin":"You need to be a server admin.",
"moderator":"You need to be a moderator.",
"support":"You need to be in the support team.",
"member":"You need to be a member.",
"discord-administrator":"You need to have the `ADMINISTRATOR` permission."
"developer":"This action is restricted to the bot developer.",
"owner":"You must be the server owner to use this.",
"admin":"You must have administrator permissions to use this.",
"moderator":"You must be a moderator to use this.",
"support":"You must be part of the support team to use this.",
"member":"You must be a server member to use this.",
"discord-administrator":"You must have the `ADMINISTRATOR` permission."
},
"actionInvalid":{
"close":"Ticket is already closed!",
"reopen":"Ticket is not closed yet!",
"claim":"Ticket is already claimed!",
"unclaim":"Ticket is not claimed yet!",
"pin":"Ticket is already pinned!",
"unpin":"Ticket is not pinned yet!",
"add":"This user is already able to access the ticket!",
"remove":"Unable to remove this user from the ticket!"
"close":"This ticket is already closed.",
"reopen":"This ticket is not closed.",
"claim":"This ticket is already claimed.",
"unclaim":"This ticket is not claimed.",
"pin":"This ticket is already pinned.",
"unpin":"This ticket is not pinned.",
"add":"This user already has access to this ticket.",
"remove":"This user does not have access to this ticket."
}
},
"params":{
@@ -432,107 +432,107 @@
}
},
"commands":{
"reason":"Specify an optional reason that will be visible in logs.",
"help":"Get a list of all the available commands.",
"panel":"Spawn a message with a dropdown or buttons (for ticket creation).",
"panelId":"The identifier of the panel that you want to spawn.",
"panelAutoUpdate":"Do you want this panel to automatically update when edited?",
"ticket":"Instantly create a ticket.",
"ticketId":"The identifier of the ticket that you want to create.",
"close":"Close a ticket.",
"delete":"Delete a ticket.",
"deleteNoTranscript":"Delete this ticket without creating a transcript.",
"reopen":"Reopen a ticket.",
"claim":"Claim a ticket.",
"claimUser":"Claim this ticket to someone else instead of yourself.",
"unclaim":"Unclaim a ticket.",
"pin":"Pin a ticket.",
"unpin":"Unpin a ticket.",
"reason":"Optional reason shown in logs.",
"help":"Display all available commands.",
"panel":"Create a ticket panel (buttons or dropdown).",
"panelId":"ID of the panel to spawn.",
"panelAutoUpdate":"Automatically update this panel when edited.",
"ticket":"Create a ticket instantly.",
"ticketId":"ID of the ticket to create.",
"close":"Close this ticket.",
"delete":"Delete this ticket.",
"deleteNoTranscript":"Delete ticket without saving a transcript.",
"reopen":"Reopen a closed ticket.",
"claim":"Claim this ticket.",
"claimUser":"Claim the ticket for another user.",
"unclaim":"Unclaim this ticket.",
"pin":"Pin this ticket.",
"unpin":"Unpin this ticket.",
"move":"Move a ticket.",
"moveId":"The identifier of the option that you want to move to.",
"rename":"Rename a ticket.",
"renameName":"The new name for this ticket.",
"add":"Add a user to a ticket.",
"addUser":"The user to add.",
"remove":"Remove a user from a ticket.",
"removeUser":"The user to remove.",
"move":"Move ticket to another option.",
"moveId":"Target option ID.",
"rename":"Rename this ticket.",
"renameName":"New name for the ticket.",
"add":"Add a user to this ticket.",
"addUser":"User to add.",
"remove":"Remove a user from this ticket.",
"removeUser":"User to remove.",
"blacklist":"Manage the ticket blacklist.",
"blacklistView":"View a list of the current blacklist.",
"blacklist":"Manage the blacklist.",
"blacklistView":"View all blacklisted users.",
"blacklistAdd":"Add a user to the blacklist.",
"blacklistRemove":"Remove a user from the blacklist.",
"blacklistGet":"Get the details from a blacklisted user.",
"blacklistGetUser":"The user to get details from.",
"stats":"View statistics from the bot, a member or a ticket.",
"statsReset":"Reset all the stats of the bot (and start counting from zero).",
"statsGlobal":"View the global stats.",
"statsUser":"View the stats from a user in the server.",
"statsUserUser":"The user to view.",
"statsTicket":"View the stats of a ticket in the server.",
"statsTicketTicket":"The ticket to view.",
"blacklistGet":"View blacklist entry details.",
"blacklistGetUser":"User to look up.",
"stats":"View bot, user or ticket statistics.",
"statsReset":"Reset all bot statistics.",
"statsGlobal":"View global bot statistics.",
"statsUser":"View a user's statistics.",
"statsUserUser":"User to view.",
"statsTicket":"View ticket statistics.",
"statsTicketTicket":"Ticket to view.",
"clear":"Delete multiple tickets at the same time.",
"clearFilter":"The filter for clearing tickets.",
"clear":"Delete multiple tickets at once.",
"clearFilter":"Filter used when clearing tickets.",
"clearFilters":{
"all":"All",
"open":"Open",
"close":"Closed",
"claim":"Claimed",
"unclaim":"Unclaimed",
"pin":"Pinned",
"unpin":"Unpinned",
"autoclose":"Autoclosed"
"all":"All tickets",
"open":"Open tickets",
"close":"Closed tickets",
"claim":"Claimed tickets",
"unclaim":"Unclaimed tickets",
"pin":"Pinned tickets",
"unpin":"Unpinned tickets",
"autoclose":"Autoclosed tickets"
},
"autoclose":"Manage autoclose in a ticket.",
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
"autocloseEnableTime":"The amount of hours this ticket needs to be inactive to close it.",
"autodelete":"Manage autodelete in a ticket.",
"autodeleteDisable":"Disable autodelete in this ticket.",
"autodeleteEnable":"Enable autodelete in this ticket.",
"autodeleteEnableTime":"The amount of days this ticket needs to be inactive to delete it.",
"autoclose":"Manage ticket autoclose.",
"autocloseDisable":"Disable autoclose.",
"autocloseEnable":"Enable autoclose.",
"autocloseEnableTime":"Hours of inactivity before closing.",
"autodelete":"Manage ticket autodelete.",
"autodeleteDisable":"Disable autodelete.",
"autodeleteEnable":"Enable autodelete.",
"autodeleteEnableTime":"Days of inactivity before deletion.",
"topic":"Manage the topic of the ticket channel.",
"topicSet":"Set the topic of the ticket channel.",
"topicValue":"The new topic of the channel.",
"topicList":"Get a list of all tickets with their topic and stats.",
"priority":"Manage the priority of the ticket.",
"prioritySet":"Set the priority of the ticket.",
"priorityValue":"The priority of the channel.",
"priorityGet":"Get the priority of the ticket.",
"priorityList":"Get a list of all tickets with their priority status.",
"transfer":"Transfer the ticket ownership from one user to another.",
"transferUser":"The user to transfer to."
"topic":"Manage ticket channel topic.",
"topicSet":"Set the ticket channel topic.",
"topicValue":"New channel topic text.",
"topicList":"List all ticket channel topics.",
"priority":"Manage ticket priority.",
"prioritySet":"Set ticket priority.",
"priorityValue":"Priority level.",
"priorityGet":"View ticket priority.",
"priorityList":"List all ticket priorities.",
"transfer":"Transfer ticket ownership.",
"transferUser":"User to transfer to."
},
"helpMenu":{
"help":"Get a list of all the available commands.",
"ticket":"Instantly create a ticket.",
"close":"Close a ticket, this disables writing in this channel.",
"delete":"Delete a ticket, this creates a transcript when enabled.",
"reopen":"Reopen a ticket, this enables writing in this channel again.",
"pin":"Pin a ticket. This will move the ticket to the top and will add a '📌' emoij to the name.",
"unpin":"Unpin a ticket. The ticket will stay on it's position but will lose the '📌' emoij.",
"move":"Move a ticket. This will change the type of this ticket.",
"rename":"Rename a ticket. This will change the channel name of this ticket.",
"claim":"Claim a ticket. With this, you can let your team know you are handling this ticket.",
"unclaim":"Unclaim a ticket. With this, you can let your team know that this ticket is free again.",
"add":"Add a user to a ticket. This will allow the user to read & write in this ticket.",
"remove":"Remove a user from a ticket. This will remove the ability to read & write for a user in this ticket.",
"panel":"Spawn a message with a dropdown or buttons (for ticket creation).",
"blacklistView":"View a list of the current blacklist.",
"help":"View all available commands.",
"ticket":"Create a ticket instantly.",
"close":"Close this ticket and disable messaging in the channel.",
"delete":"Delete this ticket (creates a transcript if enabled).",
"reopen":"Reopen a closed ticket and restore messaging.",
"pin":"Pin this ticket and move it to the top with '📌'.",
"unpin":"Unpin this ticket and remove the '📌' emoji.",
"move":"Move this ticket to a different option or ticket type.",
"rename":"Rename this ticket channel.",
"claim":"Claim this ticket to show you are handling it.",
"unclaim":"Unclaim this ticket to mark it as available.",
"add":"Add a user to this ticket.",
"remove":"Remove a user's access from this ticket.",
"panel":"Create a ticket panel with buttons or a dropdown.",
"blacklistView":"View all blacklisted users.",
"blacklistAdd":"Add a user to the blacklist.",
"blacklistRemove":"Remove a user from the blacklist.",
"blacklistGet":"Get the details from a blacklisted user.",
"statsGlobal":"View the global stats.",
"statsTicket":"View the stats of a ticket in the server.",
"statsUser":"View the stats from a user in the server.",
"statsReset":"Reset all the stats of the bot (and start counting from zero).",
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
"autodeleteDisable":"Disable autodelete in this ticket.",
"autodeleteEnable":"Enable autodelete in this ticket.",
"blacklistGet":"View details of a blacklisted user.",
"statsGlobal":"View global bot statistics.",
"statsTicket":"View statistics for a ticket.",
"statsUser":"View statistics for a user.",
"statsReset":"Reset all bot statistics.",
"autocloseDisable":"Disable autoclose for this ticket.",
"autocloseEnable":"Enable autoclose for this ticket.",
"autodeleteDisable":"Disable autodelete for this ticket.",
"autodeleteEnable":"Enable autodelete for this ticket.",
"categories":{
"general":"General Commands",
"basicTicket":"Basic Ticket Commands",
@@ -545,10 +545,10 @@
},
"stats":{
"scopes":{
"global":"Global Stats",
"system":"System Stats",
"user":"User Stats",
"ticket":"Ticket Stats",
"global":"Global Statistics",
"system":"System Statistics",
"user":"User Statistics",
"ticket":"Ticket Statistics",
"participants":"Participants",
"messages":"Messages"
},
@@ -592,9 +592,9 @@
}
},
"panel":{
"selectTicket":"Select your ticket",
"selectRole":"Select your role",
"selectOption":"Select your option"
"selectTicket":"Select a ticket",
"selectRole":"Select a role",
"selectOption":"Select an option"
},
"priorities":{
"urgent":"Urgent",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["spyeye_"],
"lastedited":"16/02/2026",
"language":"Czech",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["the_gamer"],
"lastedited":"16/02/2026",
"language":"Danish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["DJj123dj"],
"lastedited":"16/02/2026",
"language":"Dutch",
+322 -322
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["DJj123dj"],
"lastedited":"16/02/2026",
"language":"English",
@@ -13,94 +13,94 @@
"typeWarning":"[WARNING]",
"typeInfo":"[INFO]",
"headerConfigChecker":"CONFIG CHECKER",
"headerDescription":"check for errors in your config files!",
"footerError":"the bot won't start until all {0}'s are fixed!",
"footerWarning":"it's recommended to fix all {0}'s before starting!",
"headerDescription":"Validating config files...",
"footerError":"The bot will not start until all {0}'s are resolved.",
"footerWarning":"The bot may behave unexpectedly until all {0}'s are resolved.",
"footerSupport":"SUPPORT: {0} - DOCS: {1}",
"compactInformation":"use {0} for more information!",
"compactInformation":"Use {0} for a detailed config report.",
"dataPath":"path",
"dataDocs":"docs",
"dataMessages":"message"
},
"messages":{
"stringTooShort":"This string can't be shorter than {0} characters!",
"stringTooLong":"This string can't be longer than {0} characters!",
"stringLengthInvalid":"This string needs to be {0} characters long!",
"stringStartsWith":"This string needs to start with {0}!",
"stringEndsWith":"This string needs to end with {0}!",
"stringContains":"This string needs to contain {0}!",
"stringChoices":"This string can only be one of the following values: {0}!",
"stringRegex":"This string is invalid!",
"stringInvertedContains":"This string is not allowed to contain {0}!",
"stringLowercase":"This string must be written in lowercase only!",
"stringUppercase":"This string must be written in uppercase only!",
"stringSpecialCharacters":"This string is not allowed to contain any special characters! (a-z, 0-9 & space only)",
"stringNoSpaces":"This string is not allowed to contain spaces!",
"stringCapitalWord":"It's recommended that each word in this string starts with a capital letter!",
"stringCapitalSentence":"It looks like some sentences in this string don't start with a capital letter!",
"stringPunctuation":"It looks like the sentence in this string doesn't end with a punctuation mark!",
"stringTooShort":"Text must be at least {0} characters long",
"stringTooLong":"Text must be no longer than {0} characters",
"stringLengthInvalid":"Text must be exactly {0} characters long",
"stringStartsWith":"Text must start with {0}",
"stringEndsWith":"Text must end with {0}",
"stringContains":"Text must contain {0}",
"stringChoices":"Text must be one of the following: {0}",
"stringRegex":"Text does not match the required format",
"stringInvertedContains":"Text must not contain {0}",
"stringLowercase":"Text must be entirely lowercase",
"stringUppercase":"Text must be entirely uppercase",
"stringSpecialCharacters":"Text must only contain letters (a–z), numbers (0–9), and spaces",
"stringNoSpaces":"Text must not contain spaces",
"stringCapitalWord":"Each word in this value should start with a capital letter",
"stringCapitalSentence":"One or more sentences in this value do not start with a capital letter",
"stringPunctuation":"The sentence in this value does not end with a punctuation mark",
"numberTooShort":"This number can't be shorter than {0} characters!",
"numberTooLong":"This number can't be longer than {0} characters!",
"numberLengthInvalid":"This number needs to be {0} characters long!",
"numberTooSmall":"This number needs to be at least {0}!",
"numberTooLarge":"This number needs to be at most {0}!",
"numberNotEqual":"This number needs to be {0}!",
"numberStep":"This number needs to be a multiple of {0}!",
"numberStepOffset":"This number needs to be a multiple of {0} starting with {1}!",
"numberStartsWith":"This number needs to start with {0}!",
"numberEndsWith":"This number needs to end with {0}!",
"numberContains":"This number needs to contain {0}!",
"numberChoices":"This number can only be one of the following values: {0}!",
"numberFloat":"This number can't be a decimal!",
"numberNegative":"This number can't be negative!",
"numberPositive":"This number can't be positive!",
"numberZero":"This number can't be zero!",
"numberNan":"This number can't be NaN (Not A Number)!",
"numberInvertedContains":"This number is not allowed to contain {0}!",
"numberTooShort":"Number must be at least {0} digits long",
"numberTooLong":"Number must be no longer than {0} digits",
"numberLengthInvalid":"Number must be exactly {0} digits long",
"numberTooSmall":"Number must be at least {0}",
"numberTooLarge":"Number must be at most {0}",
"numberNotEqual":"Number must be exactly {0}",
"numberStep":"Number must be a multiple of {0}",
"numberStepOffset":"Number must be a multiple of {0}, starting from {1}",
"numberStartsWith":"Number must start with {0}",
"numberEndsWith":"Number must end with {0}",
"numberContains":"Number must contain {0}",
"numberChoices":"Number must be one of the following: {0}",
"numberFloat":"Number must be a whole number",
"numberNegative":"Number must be a positive number",
"numberPositive":"Number must be a negative number",
"numberZero":"Number must not be zero",
"numberNan":"Number must be a valid number",
"numberInvertedContains":"Number must not contain {0}",
"booleanTrue":"This boolean can't be true!",
"booleanFalse":"This boolean can't be false!",
"booleanTrue":"Boolean must be false",
"booleanFalse":"Boolean must be true",
"arrayEmptyDisabled":"List must not be empty",
"arrayEmptyRequired":"List must be empty",
"arrayTooShort":"List must have at least {0} items",
"arrayTooLong":"List must have at most {0} items",
"arrayLengthInvalid":"List must have exactly {0} items",
"arrayInvalidTypes":"List may only contain the following types: {0}",
"arrayDouble":"List must not contain duplicate values",
"discordInvalidId":"Invalid Discord {0} ID",
"discordInvalidIdOptions":"Invalid Discord {0} ID. Alternatively, use one of the following: {1}",
"discordInvalidToken":"Invalid Discord token",
"colorInvalid":"Invalid hex color",
"emojiTooShort":"Value must contain at least {0} emoji",
"emojiTooLong":"Value must contain at most {0} emoji",
"emojiCustom":"Custom Discord emojis are not allowed here",
"emojiInvalid":"Invalid emoji",
"urlInvalid":"Invalid URL",
"urlInvalidHttp":"URL must use the https:// protocol",
"urlInvalidProtocol":"URL must use the http:// or https:// protocol",
"urlInvalidHostname":"URL hostname is not allowed",
"urlInvalidExtension":"Invalid URL extension. Allowed extensions: {0}",
"urlInvalidPath":"Invalid URL path",
"idNotUnique":"This ID is already in use. Please choose a unique ID",
"idNonExistent":"ID {0} does not exist",
"invalidType":"Property must be of type: {0}",
"propertyMissing":"Required property {0} is missing from the object",
"propertyOptional":"Property {0} is optional in the object",
"objectDisabled":"This object is disabled. Enable it using {0}",
"nullInvalid":"Property must not be null",
"switchInvalidType":"Value must be one of the following types: {0}",
"objectSwitchInvalid":"Object must be one of the following types: {0}",
"arrayEmptyDisabled":"This array isn't allowed to be empty!",
"arrayEmptyRequired":"This array is required to be empty!",
"arrayTooShort":"This array needs to have a length of at least {0}!",
"arrayTooLong":"This array needs to have a length of at most {0}!",
"arrayLengthInvalid":"This array needs to have a length of {0}!",
"arrayInvalidTypes":"This array can only contain the following types: {0}!",
"arrayDouble":"This array doesn't allow the same value twice!",
"discordInvalidId":"This is an invalid discord {0} id!",
"discordInvalidIdOptions":"This is an invalid discord {0} id! You can also use one of these: {1}!",
"discordInvalidToken":"This is an invalid discord token (syntactically)!",
"colorInvalid":"This is an invalid hex color!",
"emojiTooShort":"This string needs to have at least {0} emoji's!",
"emojiTooLong":"This string needs to have at most {0} emoji's!",
"emojiCustom":"This emoji can't be a custom discord emoji!",
"emojiInvalid":"This is an invalid emoji!",
"urlInvalid":"This url is invalid!",
"urlInvalidHttp":"This url can only use the https:// protocol!",
"urlInvalidProtocol":"This url can only use the http:// & https:// protocols!",
"urlInvalidHostname":"This url has a disallowed hostname!",
"urlInvalidExtension":"This url has an invalid extension! Choose between: {0}!",
"urlInvalidPath":"This url has an invalid path!",
"idNotUnique":"This id isn't unique, use another id instead!",
"idNonExistent":"The id {0} doesn't exist!",
"invalidType":"This property needs to be the type: {0}!",
"propertyMissing":"The property {0} is missing from this object!",
"propertyOptional":"The property {0} is optional in this object!",
"objectDisabled":"This object is disabled, enable it using {0}!",
"nullInvalid":"This property can't be null!",
"switchInvalidType":"This needs to be one of the following types: {0}!",
"objectSwitchInvalid":"This object needs to be one of the following types: {0}!",
"invalidLanguage":"This is an invalid language!",
"invalidButton":"This button needs to have at least an {0} or {1}!",
"unusedOption":"The option {0} isn't used anywhere!",
"unusedQuestion":"The question {0} isn't used anywhere!",
"dropdownOption":"A panel with dropdown enabled can only contain options of the 'ticket' type!",
"customInvalidVersion":"The version specified in your config does not match! Make sure you have updated the config to the latest version!"
"invalidLanguage":"Invalid language",
"invalidButton":"Button must have at least an {0} or {1}",
"unusedOption":"Option {0} is not used anywhere",
"unusedQuestion":"Question {0} is not used anywhere",
"dropdownOption":"Panels with dropdown enabled may only contain options of the 'ticket' type",
"customInvalidVersion":"Config version mismatch. Make sure to update your config to the latest version"
}
},
"actions":{
@@ -159,118 +159,118 @@
"transfer":"Ticket Transferred"
},
"descriptions":{
"create":"Your ticket has been created. Click the button below to access it!",
"close":"The ticket has been closed successfully!",
"delete":"The ticket has been deleted successfully!",
"reopen":"The ticket has been reopened successfully!",
"claim":"The ticket has been claimed successfully!",
"unclaim":"The ticket has been unclaimed successfully!",
"pin":"The ticket has been pinned successfully!",
"unpin":"The ticket has been unpinned successfully!",
"rename":"The ticket has been renamed to {0} successfully!",
"move":"The ticket has been moved to {0} successfully!",
"add":"{0} has been added to the ticket successfully!",
"remove":"{0} has been removed from the ticket successfully!",
"create":"Your ticket is ready. Click the button below to view and continue.",
"close":"The ticket has been closed.",
"delete":"The ticket has been deleted.",
"reopen":"The ticket has been reopened.",
"claim":"The ticket has been claimed.",
"unclaim":"The ticket has been unclaimed.",
"pin":"The ticket has been pinned.",
"unpin":"The ticket has been unpinned.",
"rename":"The ticket has been renamed to {0}.",
"move":"The ticket has been moved to {0}.",
"add":"{0} has been added to the ticket.",
"remove":"{0} has been removed from the ticket.",
"helpExplanation":"`<name>` => required parameter\n`[name]` => optional parameter",
"statsReset":"The bot stats have been reset successfully!",
"statsError":"Unable to view ticket stats!\n{0} is not a ticket!",
"blacklistAdd":"{0} has been blacklisted successfully!",
"blacklistRemove":"{0} has been released successfully!",
"blacklistGetSuccess":"{0} is currently blacklisted!",
"blacklistGetEmpty":"{0} is currently not blacklisted!",
"blacklistViewEmpty":"No-one has been blacklisted yet!",
"blacklistViewTip":"Use \"/blacklist add\" to blacklist a user!",
"clearVerify":"Are you sure you want to delete multiple tickets?\nThis action can't be undone!",
"clearReady":"{0} tickets have been deleted successfully!",
"rolesEmpty":"No roles have been updated!",
"statsReset":"The bot statistics have been reset.",
"statsError":"Unable to retrieve ticket statistics.\n{0} is not a valid ticket.",
"blacklistAdd":"{0} has been blacklisted.",
"blacklistRemove":"{0} has been released.",
"blacklistGetSuccess":"{0} is blacklisted!",
"blacklistGetEmpty":"{0} is not blacklisted!",
"blacklistViewEmpty":"No users have been blacklisted yet.",
"blacklistViewTip":"Use \"/blacklist add\" to add a user to the blacklist.",
"clearVerify":"Are you sure you want to delete multiple tickets?\nThis action cannot be undone.",
"clearReady":"{0} ticket(s) have been deleted.",
"rolesEmpty":"No roles were modified.",
"autocloseLeave":"This ticket has been autoclosed because the creator left the server!",
"autocloseTimeout":"This ticket has been autoclosed because it has been inactive for more than `{0}h`!",
"autodeleteLeave":"This ticket has been autodeleted because the creator left the server!",
"autodeleteTimeout":"This ticket has been autodeleted because it has been inactive for more than `{0} days`!",
"autocloseEnabled":"Autoclose has been enabled in this ticket!\nIt will be closed when it is inactive for more than `{0}h`!",
"autocloseDisabled":"Autoclose has been disabled in this ticket!\nIt won't be closed automatically anymore!",
"autodeleteEnabled":"Autodelete has been enabled in this ticket!\nIt will be deleted when it is inactive for more than `{0} days`!",
"autodeleteDisabled":"Autodelete has been disabled in this ticket!\nIt won't be deleted automatically anymore!",
"autocloseLeave":"This ticket was automatically closed because its creator left the server.",
"autocloseTimeout":"This ticket was automatically closed due to inactivity exceeding `{0}h`.",
"autodeleteLeave":"This ticket was automatically deleted because its creator left the server.",
"autodeleteTimeout":"This ticket was automatically deleted due to inactivity exceeding `{0} days`.",
"autocloseEnabled":"Autoclose has been enabled for this ticket.\nIt will close after `{0}h` of inactivity.",
"autocloseDisabled":"Autoclose has been disabled for this ticket.\nThis ticket will no longer close automatically.",
"autodeleteEnabled":"Autodelete has been enabled for this ticket.\nIt will be deleted after `{0} days` of inactivity.",
"autodeleteDisabled":"Autodelete has been disabled for this ticket.\nThis ticket will no longer be deleted automatically.",
"ticketMessageLimit":"You can only create {0} ticket(s) at the same time!",
"ticketMessageAutoclose":"This ticket will be autoclosed when inactive for {0}h!",
"ticketMessageAutodelete":"This ticket will be autodeleted when inactive for {0} days!",
"panelReady":"The panel is available in the followup message!\nThis message can now be deleted!",
"ticketMessageLimit":"You can only have {0} active ticket(s) at a time.",
"ticketMessageAutoclose":"This ticket will automatically close after `{0}h` of inactivity.",
"ticketMessageAutodelete":"This ticket will automatically be deleted after `{0} days` of inactivity.",
"panelReady":"The panel has been sent in the follow-up message.\nYou may now delete this message.",
"topicSet":"The channel topic has been changed by {0} successfully!",
"prioritySet":"The ticket priority has been changed to {0} by {1} successfully!",
"priorityGet":"The current priority of this ticket is {0}.",
"transfer":"The ticket ownership has been transferred from {0} to {1} by {2} successfully!"
"topicSet":"The channel topic has been changed by {0}.",
"prioritySet":"The ticket priority has been changed to {0} by {1}.",
"priorityGet":"The priority of this ticket is {0}.",
"transfer":"The ticket ownership has been transferred from {0} to {1} by {2}."
},
"modal":{
"closePlaceholder":"Why did you close this ticket?",
"deletePlaceholder":"Why did you delete this ticket?",
"reopenPlaceholder":"Why did you reopen this ticket?",
"claimPlaceholder":"Why did you claim this ticket?",
"unclaimPlaceholder":"Why did you unclaim this ticket?",
"pinPlaceholder":"Why did you pin this ticket?",
"unpinPlaceholder":"Why did you unpin this ticket?"
"closePlaceholder":"Why would you like to close this ticket?",
"deletePlaceholder":"Why would you like to delete this ticket?",
"reopenPlaceholder":"Why would you like to reopen this ticket?",
"claimPlaceholder":"Why would you like to claim this ticket?",
"unclaimPlaceholder":"Why would you like to unclaim this ticket?",
"pinPlaceholder":"Why would you like to pin this ticket?",
"unpinPlaceholder":"Why would you like to unpin this ticket?"
},
"logs":{
"createLog":"A new ticket got created by {0}!",
"closeLog":"This ticket has been closed by {0}!",
"closeDm":"Your ticket has been closed in our server!",
"deleteLog":"This ticket has been deleted by {0}!",
"deleteDm":"Your ticket has been deleted in our server!",
"reopenLog":"This ticket has been reopened by {0}!",
"reopenDm":"Your ticket has been reopened in our server!",
"claimLog":"This ticket has been claimed by {0}!",
"claimDm":"Your ticket has been claimed in our server!",
"unclaimLog":"This ticket has been unclaimed by {0}!",
"unclaimDm":"Your ticket has been unclaimed in our server!",
"pinLog":"This ticket has been pinned by {0}!",
"pinDm":"Your ticket has been pinned in our server!",
"unpinLog":"This ticket has been unpinned by {0}!",
"unpinDm":"Your ticket has been unpinned in our server!",
"renameLog":"This ticket has been renamed to {0} by {1}!",
"renameDm":"Your ticket has been renamed to {0} in our server!",
"moveLog":"This ticket has been moved to {0} by {1}!",
"moveDm":"Your ticket has been moved to {0} in our server!",
"addLog":"{0} has been added to this ticket by {1}!",
"addDm":"{0} has been added to your ticket in our server!",
"removeLog":"{0} has been removed from this ticket by {1}!",
"removeDm":"{0} has been removed from your ticket in our server!",
"createLog":"A new ticket got created by {0}.",
"closeLog":"This ticket has been closed by {0}.",
"closeDm":"Your ticket has been closed.",
"deleteLog":"This ticket has been deleted by {0}.",
"deleteDm":"Your ticket has been deleted.",
"reopenLog":"This ticket has been reopened by {0}.",
"reopenDm":"Your ticket has been reopened.",
"claimLog":"This ticket has been claimed by {0}.",
"claimDm":"Your ticket has been claimed.",
"unclaimLog":"This ticket has been unclaimed by {0}.",
"unclaimDm":"Your ticket has been unclaimed.",
"pinLog":"This ticket has been pinned by {0}.",
"pinDm":"Your ticket has been pinned.",
"unpinLog":"This ticket has been unpinned by {0}.",
"unpinDm":"Your ticket has been unpinned.",
"renameLog":"This ticket has been renamed to {0} by {1}.",
"renameDm":"Your ticket has been renamed to {0}.",
"moveLog":"This ticket has been moved to {0} by {1}.",
"moveDm":"Your ticket has been moved to {0}.",
"addLog":"{0} has been added to this ticket by {1}.",
"addDm":"{0} has been added to your ticket.",
"removeLog":"{0} has been removed from this ticket by {1}.",
"removeDm":"{0} has been removed from your ticket.",
"blacklistAddLog":"{0} was blacklisted by {1}!",
"blacklistRemoveLog":"{0} was removed from the blacklist by {1}!",
"blacklistAddDm":"You have been blacklisted in our server!\nFrom now on, you are unable to create a ticket!",
"blacklistRemoveDm":"You have been removed from the blacklist in our server!\nNow you can create tickets again!",
"clearLog":"{0} tickets have been deleted by {1}!",
"blacklistAddLog":"{0} has been blacklisted by {1}.",
"blacklistRemoveLog":"{0} has been removed from the blacklist by {1}.",
"blacklistAddDm":"You have been blacklisted from this server.\nYou are no longer able to create tickets.",
"blacklistRemoveDm":"You have been removed from the blacklist.\nYou can now create tickets again.",
"clearLog":"{0} ticket(s) have been deleted by {1}.",
"transferLog":"The ownership of this ticket has been transferred from {0} to {1} by {2}!",
"transferDm":"The ownership of your ticket has been transferred from {0} to {1} in our server!",
"prioritySetLog":"The priority of this ticket has been changed to {0} by {1}!",
"prioritySetDm":"The priority of your ticket has been changed to {0} in our server!",
"roleUpdateLog":"{0} has updated their roles!",
"roleUpdateDm":"Your roles in our server have been updated!"
"transferLog":"Ticket ownership has been transferred from {0} to {1} by {2}.",
"transferDm":"Ownership of your ticket has been transferred from {0} to {1}.",
"prioritySetLog":"The priority of this ticket has been set to {0} by {1}.",
"prioritySetDm":"The priority of your ticket has been set to {0}.",
"roleUpdateLog":"{0} has modified their roles.",
"roleUpdateDm":"Your roles have been modified."
}
},
"transcripts":{
"success":{
"visit":"Visit Transcript",
"ready":"Transcript Created",
"textFileDescription":"This is the text transcript of a deleted ticket!",
"htmlProgress":"Please wait while this html transcript is getting processed...",
"createdChannel":"A new {0} transcript has been created in the server!",
"createdCreator":"A new {0} transcript has been created for one of your tickets!",
"createdParticipant":"A new {0} transcript has been created in one of the tickets you participated in!",
"createdActiveAdmin":"A new {0} transcript has been created in one of the tickets you participated as admin!",
"createdEveryAdmin":"A new {0} transcript has been created in one of the tickets you were admin in!",
"createdOther":"A new {0} transcript has been created!"
"textFileDescription":"This is a text transcript of a deleted ticket.",
"htmlProgress":"Please wait while the HTML transcript is being generated...",
"createdChannel":"A new {0} transcript has been created in the server.",
"createdCreator":"A new {0} transcript has been created for one of your tickets.",
"createdParticipant":"A new {0} transcript has been created for a ticket you participated in.",
"createdActiveAdmin":"A new {0} transcript has been created for a ticket you participated in as an admin.",
"createdEveryAdmin":"A new {0} transcript has been created for a ticket you managed as an admin.",
"createdOther":"A new {0} transcript has been created."
},
"errors":{
"retry":"Retry",
"continue":"Delete Without Transcript",
"backup":"Create Backup Transcript",
"error":"Something went wrong while trying to create the transcript.\nWhat would you like to do?\n\nThis ticket won't be deleted until you click one of these buttons.",
"error":"Something went wrong while creating the transcript.\nWhat would you like to do?\n\nThis ticket will not be deleted until you select one of the options below.",
"title":"Transcript Error"
},
"text":{
@@ -279,7 +279,7 @@
"fileTitle":"FILE",
"fieldsTitle":"FIELDS",
"reactionsTitle":"REACTIONS",
"statsTitle":"STATS",
"statsTitle":"STATISTICS",
"emptyContent":"<content is empty>",
"noTitle":"<no-title>",
"noDesc":"<no-description>"
@@ -302,68 +302,68 @@
"permissionError":"Permission Error"
},
"descriptions":{
"askForInfo":"Contact the owner of this bot for more info!",
"askForInfoResolve":"Contact the bot owner of this bot if this issue doesn't resolve after a few tries.",
"internalError":"Failed to respond to this {0} due to an internal error!",
"optionMissing":"A required parameter is missing in this command!",
"optionInvalid":"A parameter in this command is invalid!",
"optionInvalidChoose":"Choose between",
"unknownCommand":"Try visiting the help menu for more info!",
"noPermissions":"You are not allowed to use this {0}!",
"noPermissionsList":"Required Permissions: (one of them)",
"noPermissionsCooldown":"You are not allowed to use this {0} because you have a cooldown!",
"noPermissionsBlacklist":"You are not allowed to use this {0} because you have been blacklisted!",
"noPermissionsLimitGlobal":"You are not allowed to create a ticket because the server reached the max tickets limit!",
"noPermissionsLimitGlobalUser":"You are not allowed to create a ticket because you reached the max tickets limit!",
"noPermissionsLimitOption":"You are not allowed to create a ticket because the server reached the max tickets limit for this option!",
"noPermissionsLimitOptionUser":"You are not allowed to create a ticket because you reached the max tickets limit for this option!",
"unknownTicket":"Try this command again in a valid ticket!",
"deprecatedTicket":"The current channel is not a valid ticket! It might have been a ticket from an old Open Ticket version!",
"notInGuild":"This {0} doesn't work in DM! Please try it again in a server!",
"channelRename":"Due to discord ratelimits, it's currently impossible for the bot to rename the channel. The channel will automatically be renamed over 10 minutes if the bot isn't rebooted.",
"channelRenameSource":"The source of this error is: {0}",
"busy":"Unable to use this {0}!\nThe ticket is currently being processed by the bot.\n\nPlease try again in a few seconds!",
"closeBeforeMessage":"This ticket cannot be closed/deleted before a message has been sent by a user.",
"closeBeforeAdminMessage":"This ticket cannot be closed/deleted before a message has been sent by a ticket admin or support member.",
"unableToCreateTicket":"You are unable to create a ticket."
"askForInfo":"Please contact the bot owner for more information.",
"askForInfoResolve":"If the issue persists after a few attempts, please contact the bot owner.",
"internalError":"An internal error occurred while processing this {0}.",
"optionMissing":"A required parameter is missing for this command.",
"optionInvalid":"One or more parameters provided for this command are invalid.",
"optionInvalidChoose":"Please choose between",
"unknownCommand":"Please use the help menu for more information.",
"noPermissions":"You are not permitted to use this {0}.",
"noPermissionsList":"Required permissions (one of the following):",
"noPermissionsCooldown":"You cannot use this {0} while on cooldown.",
"noPermissionsBlacklist":"You cannot use this {0} because you are blacklisted.",
"noPermissionsLimitGlobal":"You cannot create a ticket because the server has reached its maximum ticket limit.",
"noPermissionsLimitGlobalUser":"You cannot create a ticket because you have reached your maximum ticket limit.",
"noPermissionsLimitOption":"You cannot create a ticket because the server has reached the maximum ticket limit for this option.",
"noPermissionsLimitOptionUser":"You cannot create a ticket because you have reached the maximum ticket limit for this option.",
"unknownTicket":"Please run this command inside a valid ticket channel.",
"deprecatedTicket":"This channel is not recognized as a valid ticket. It may have been created with an older version of the bot.",
"notInGuild":"This {0} cannot be used in direct messages. Please use it in a server.",
"channelRename":"Due to Discord rate limits, the channel could not be renamed immediately. It will be renamed automatically within 10 minutes if the bot remains online.",
"channelRenameSource":"Error source: {0}",
"busy":"This {0} is currently unavailable.\nThe ticket is being processed by the bot.\n\nPlease try again in a few seconds.",
"closeBeforeMessage":"This ticket cannot be closed or deleted before a user has sent a message.",
"closeBeforeAdminMessage":"This ticket cannot be closed or deleted before a support member or admin has sent a message.",
"unableToCreateTicket":"You are currently unable to create a ticket."
},
"optionInvalidReasons":{
"stringRegex":"Value doesn't match pattern!",
"stringMinLength":"Value needs to be at least {0} characters!",
"stringMaxLength":"Value needs to be at most {0} characters!",
"numberInvalid":"Invalid number!",
"numberMin":"Number needs to be at least {0}!",
"numberMax":"Number needs to be at most {0}!",
"numberDecimal":"Number is not allowed to be a decimal!",
"numberNegative":"Number is not allowed to be negative!",
"numberPositive":"Number is not allowed to be positive!",
"numberZero":"Number is not allowed to be zero!",
"channelNotFound":"Unable to find channel!",
"userNotFound":"Unable to find user!",
"roleNotFound":"Unable to find role!",
"memberNotFound":"Unable to find user!",
"mentionableNotFound":"Unable to find user or role!",
"channelType":"Invalid channel type!",
"notInGuild":"This option requires you to be in a server!"
"stringRegex":"The value does not match the required pattern.",
"stringMinLength":"The value must be at least {0} characters long.",
"stringMaxLength":"The value must be at most {0} characters long.",
"numberInvalid":"Invalid number provided.",
"numberMin":"The number must be at least {0}.",
"numberMax":"The number must be at most {0}.",
"numberDecimal":"Decimals are not allowed.",
"numberNegative":"Negative numbers are not allowed.",
"numberPositive":"Positive numbers are not allowed.",
"numberZero":"Zero is not allowed.",
"channelNotFound":"Channel not found.",
"userNotFound":"User not found.",
"roleNotFound":"Role not found.",
"memberNotFound":"Member not found.",
"mentionableNotFound":"User or role not found.",
"channelType":"Invalid channel type.",
"notInGuild":"This option can only be used in a server."
},
"permissions":{
"developer":"You need to be the developer of the bot.",
"owner":"You need to be the server owner.",
"admin":"You need to be a server admin.",
"moderator":"You need to be a moderator.",
"support":"You need to be in the support team.",
"member":"You need to be a member.",
"discord-administrator":"You need to have the `ADMINISTRATOR` permission."
"developer":"This action is restricted to the bot developer.",
"owner":"You must be the server owner to use this.",
"admin":"You must have administrator permissions to use this.",
"moderator":"You must be a moderator to use this.",
"support":"You must be part of the support team to use this.",
"member":"You must be a server member to use this.",
"discord-administrator":"You must have the `ADMINISTRATOR` permission."
},
"actionInvalid":{
"close":"Ticket is already closed!",
"reopen":"Ticket is not closed yet!",
"claim":"Ticket is already claimed!",
"unclaim":"Ticket is not claimed yet!",
"pin":"Ticket is already pinned!",
"unpin":"Ticket is not pinned yet!",
"add":"This user is already able to access the ticket!",
"remove":"Unable to remove this user from the ticket!"
"close":"This ticket is already closed.",
"reopen":"This ticket is not closed.",
"claim":"This ticket is already claimed.",
"unclaim":"This ticket is not claimed.",
"pin":"This ticket is already pinned.",
"unpin":"This ticket is not pinned.",
"add":"This user already has access to this ticket.",
"remove":"This user does not have access to this ticket."
}
},
"params":{
@@ -432,107 +432,107 @@
}
},
"commands":{
"reason":"Specify an optional reason that will be visible in logs.",
"help":"Get a list of all the available commands.",
"panel":"Spawn a message with a dropdown or buttons (for ticket creation).",
"panelId":"The identifier of the panel that you want to spawn.",
"panelAutoUpdate":"Do you want this panel to automatically update when edited?",
"ticket":"Instantly create a ticket.",
"ticketId":"The identifier of the ticket that you want to create.",
"close":"Close a ticket.",
"delete":"Delete a ticket.",
"deleteNoTranscript":"Delete this ticket without creating a transcript.",
"reopen":"Reopen a ticket.",
"claim":"Claim a ticket.",
"claimUser":"Claim this ticket to someone else instead of yourself.",
"unclaim":"Unclaim a ticket.",
"pin":"Pin a ticket.",
"unpin":"Unpin a ticket.",
"reason":"Optional reason shown in logs.",
"help":"Display all available commands.",
"panel":"Create a ticket panel (buttons or dropdown).",
"panelId":"ID of the panel to spawn.",
"panelAutoUpdate":"Automatically update this panel when edited.",
"ticket":"Create a ticket instantly.",
"ticketId":"ID of the ticket to create.",
"close":"Close this ticket.",
"delete":"Delete this ticket.",
"deleteNoTranscript":"Delete ticket without saving a transcript.",
"reopen":"Reopen a closed ticket.",
"claim":"Claim this ticket.",
"claimUser":"Claim the ticket for another user.",
"unclaim":"Unclaim this ticket.",
"pin":"Pin this ticket.",
"unpin":"Unpin this ticket.",
"move":"Move a ticket.",
"moveId":"The identifier of the option that you want to move to.",
"rename":"Rename a ticket.",
"renameName":"The new name for this ticket.",
"add":"Add a user to a ticket.",
"addUser":"The user to add.",
"remove":"Remove a user from a ticket.",
"removeUser":"The user to remove.",
"move":"Move ticket to another option.",
"moveId":"Target option ID.",
"rename":"Rename this ticket.",
"renameName":"New name for the ticket.",
"add":"Add a user to this ticket.",
"addUser":"User to add.",
"remove":"Remove a user from this ticket.",
"removeUser":"User to remove.",
"blacklist":"Manage the ticket blacklist.",
"blacklistView":"View a list of the current blacklist.",
"blacklist":"Manage the blacklist.",
"blacklistView":"View all blacklisted users.",
"blacklistAdd":"Add a user to the blacklist.",
"blacklistRemove":"Remove a user from the blacklist.",
"blacklistGet":"Get the details from a blacklisted user.",
"blacklistGetUser":"The user to get details from.",
"stats":"View statistics from the bot, a member or a ticket.",
"statsReset":"Reset all the stats of the bot (and start counting from zero).",
"statsGlobal":"View the global stats.",
"statsUser":"View the stats from a user in the server.",
"statsUserUser":"The user to view.",
"statsTicket":"View the stats of a ticket in the server.",
"statsTicketTicket":"The ticket to view.",
"blacklistGet":"View blacklist entry details.",
"blacklistGetUser":"User to look up.",
"stats":"View bot, user or ticket statistics.",
"statsReset":"Reset all bot statistics.",
"statsGlobal":"View global bot statistics.",
"statsUser":"View a user's statistics.",
"statsUserUser":"User to view.",
"statsTicket":"View ticket statistics.",
"statsTicketTicket":"Ticket to view.",
"clear":"Delete multiple tickets at the same time.",
"clearFilter":"The filter for clearing tickets.",
"clear":"Delete multiple tickets at once.",
"clearFilter":"Filter used when clearing tickets.",
"clearFilters":{
"all":"All",
"open":"Open",
"close":"Closed",
"claim":"Claimed",
"unclaim":"Unclaimed",
"pin":"Pinned",
"unpin":"Unpinned",
"autoclose":"Autoclosed"
"all":"All tickets",
"open":"Open tickets",
"close":"Closed tickets",
"claim":"Claimed tickets",
"unclaim":"Unclaimed tickets",
"pin":"Pinned tickets",
"unpin":"Unpinned tickets",
"autoclose":"Autoclosed tickets"
},
"autoclose":"Manage autoclose in a ticket.",
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
"autocloseEnableTime":"The amount of hours this ticket needs to be inactive to close it.",
"autodelete":"Manage autodelete in a ticket.",
"autodeleteDisable":"Disable autodelete in this ticket.",
"autodeleteEnable":"Enable autodelete in this ticket.",
"autodeleteEnableTime":"The amount of days this ticket needs to be inactive to delete it.",
"autoclose":"Manage ticket autoclose.",
"autocloseDisable":"Disable autoclose.",
"autocloseEnable":"Enable autoclose.",
"autocloseEnableTime":"Hours of inactivity before closing.",
"autodelete":"Manage ticket autodelete.",
"autodeleteDisable":"Disable autodelete.",
"autodeleteEnable":"Enable autodelete.",
"autodeleteEnableTime":"Days of inactivity before deletion.",
"topic":"Manage the topic of the ticket channel.",
"topicSet":"Set the topic of the ticket channel.",
"topicValue":"The new topic of the channel.",
"topicList":"Get a list of all tickets with their topic and stats.",
"priority":"Manage the priority of the ticket.",
"prioritySet":"Set the priority of the ticket.",
"priorityValue":"The priority of the channel.",
"priorityGet":"Get the priority of the ticket.",
"priorityList":"Get a list of all tickets with their priority status.",
"transfer":"Transfer the ticket ownership from one user to another.",
"transferUser":"The user to transfer to."
"topic":"Manage ticket channel topic.",
"topicSet":"Set the ticket channel topic.",
"topicValue":"New channel topic text.",
"topicList":"List all ticket channel topics.",
"priority":"Manage ticket priority.",
"prioritySet":"Set ticket priority.",
"priorityValue":"Priority level.",
"priorityGet":"View ticket priority.",
"priorityList":"List all ticket priorities.",
"transfer":"Transfer ticket ownership.",
"transferUser":"User to transfer to."
},
"helpMenu":{
"help":"Get a list of all the available commands.",
"ticket":"Instantly create a ticket.",
"close":"Close a ticket, this disables writing in this channel.",
"delete":"Delete a ticket, this creates a transcript when enabled.",
"reopen":"Reopen a ticket, this enables writing in this channel again.",
"pin":"Pin a ticket. This will move the ticket to the top and will add a '📌' emoij to the name.",
"unpin":"Unpin a ticket. The ticket will stay on it's position but will lose the '📌' emoij.",
"move":"Move a ticket. This will change the type of this ticket.",
"rename":"Rename a ticket. This will change the channel name of this ticket.",
"claim":"Claim a ticket. With this, you can let your team know you are handling this ticket.",
"unclaim":"Unclaim a ticket. With this, you can let your team know that this ticket is free again.",
"add":"Add a user to a ticket. This will allow the user to read & write in this ticket.",
"remove":"Remove a user from a ticket. This will remove the ability to read & write for a user in this ticket.",
"panel":"Spawn a message with a dropdown or buttons (for ticket creation).",
"blacklistView":"View a list of the current blacklist.",
"help":"View all available commands.",
"ticket":"Create a ticket instantly.",
"close":"Close this ticket and disable messaging in the channel.",
"delete":"Delete this ticket (creates a transcript if enabled).",
"reopen":"Reopen a closed ticket and restore messaging.",
"pin":"Pin this ticket and move it to the top with '📌'.",
"unpin":"Unpin this ticket and remove the '📌' emoji.",
"move":"Move this ticket to a different option or ticket type.",
"rename":"Rename this ticket channel.",
"claim":"Claim this ticket to show you are handling it.",
"unclaim":"Unclaim this ticket to mark it as available.",
"add":"Add a user to this ticket.",
"remove":"Remove a user's access from this ticket.",
"panel":"Create a ticket panel with buttons or a dropdown.",
"blacklistView":"View all blacklisted users.",
"blacklistAdd":"Add a user to the blacklist.",
"blacklistRemove":"Remove a user from the blacklist.",
"blacklistGet":"Get the details from a blacklisted user.",
"statsGlobal":"View the global stats.",
"statsTicket":"View the stats of a ticket in the server.",
"statsUser":"View the stats from a user in the server.",
"statsReset":"Reset all the stats of the bot (and start counting from zero).",
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
"autodeleteDisable":"Disable autodelete in this ticket.",
"autodeleteEnable":"Enable autodelete in this ticket.",
"blacklistGet":"View details of a blacklisted user.",
"statsGlobal":"View global bot statistics.",
"statsTicket":"View statistics for a ticket.",
"statsUser":"View statistics for a user.",
"statsReset":"Reset all bot statistics.",
"autocloseDisable":"Disable autoclose for this ticket.",
"autocloseEnable":"Enable autoclose for this ticket.",
"autodeleteDisable":"Disable autodelete for this ticket.",
"autodeleteEnable":"Enable autodelete for this ticket.",
"categories":{
"general":"General Commands",
"basicTicket":"Basic Ticket Commands",
@@ -545,10 +545,10 @@
},
"stats":{
"scopes":{
"global":"Global Stats",
"system":"System Stats",
"user":"User Stats",
"ticket":"Ticket Stats",
"global":"Global Statistics",
"system":"System Statistics",
"user":"User Statistics",
"ticket":"Ticket Statistics",
"participants":"Participants",
"messages":"Messages"
},
@@ -592,9 +592,9 @@
}
},
"panel":{
"selectTicket":"Select your ticket",
"selectRole":"Select your role",
"selectOption":"Select your option"
"selectTicket":"Select a ticket",
"selectRole":"Select a role",
"selectOption":"Select an option"
},
"priorities":{
"urgent":"Urgent",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["iamnotmega","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Estonian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["iamnotmega","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Finnish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["guillee3"],
"lastedited":"16/02/2026",
"language":"French",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["benzorich"],
"lastedited":"16/02/2026",
"language":"German",
@@ -14,8 +14,8 @@
"typeInfo":"[INFO]",
"headerConfigChecker":"CONFIG CHECKER",
"headerDescription":"Prüfen Sie auf Fehler in Ihrer Konfigurationsdateien!",
"footerError":"Der Bot wird nicht starten, bis alle {0}'s repariert sind!",
"footerWarning":"Es wird empfohlen, vor dem Start alle {0}'s zu korrigieren!",
"footerError":"Der Bot wird nicht starten, bis alle {0}en repariert sind!",
"footerWarning":"Es wird empfohlen, vor dem Start alle {0}en zu korrigieren!",
"footerSupport":"SUPPORT: {0} - DOCS: {1}",
"compactInformation":"Verwenden Sie {0} für weitere Informationen!",
"dataPath":"Pfad",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Greek",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["challenger_nova"],
"lastedited":"16/02/2026",
"language":"Hindi",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["Kornel0706"],
"lastedited":"16/02/2026",
"language":"Hungarian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["erxg"],
"lastedited":"16/02/2026",
"language":"Indonesian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["fraden1mvp.","imperatorix_17"],
"lastedited":"16/02/2026",
"language":"Italian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Japanese",
+599 -599
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Korean",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Kurdish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Latvian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["TsgIndrius"],
"lastedited":"16/02/2026",
"language":"Lithuanian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Norwegian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["dysashop","zhavis"],
"lastedited":"16/02/2026",
"language":"Persian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["DanoGlez"],
"lastedited":"16/02/2026",
"language":"Polish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["quiradon"],
"lastedited":"16/02/2026",
"language":"Portuguese",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["SankeDev"],
"lastedited":"16/02/2026",
"language":"Romanian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Russian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Simplified Chainese",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Solvenian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["Redactado","Josuens"],
"lastedited":"16/02/2026",
"language":"Spanish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["NoOneNook"],
"lastedited":"16/02/2026",
"language":"Svenska",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["HanumeshGupta","ChatGPT"],
"lastedited":"16/02/2026",
"language":"Tamil",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["modshd"],
"lastedited":"16/02/2026",
"language":"Thai",
+608
View File
@@ -0,0 +1,608 @@
{
"_TRANSLATION":{
"otversion":"v4.2.0",
"translators":["me.october"],
"lastedited":"10/03/2026",
"language":"Traditional Chinese",
"automated":false
},
"checker":{
"system":{
"typeError":"[錯誤]",
"headerOpenTicket":"開啟工單",
"typeWarning":"[警告]",
"typeInfo":"[信息]",
"headerConfigChecker":"配置檢查器",
"headerDescription":"檢查您的配置文件中的錯誤!",
"footerError":"機器人將無法啟動,直到所有{0}錯誤被修複!",
"footerWarning":"建議在啟動前修複所有{0}警告!",
"footerSupport":"支援:{0} - 文檔:{1}",
"compactInformation":"使用{0}獲取更多信息!",
"dataPath":"路徑",
"dataDocs":"文檔",
"dataMessages":"消息"
},
"messages":{
"stringTooShort":"此字符串不能少於{0}個字符!",
"stringTooLong":"此字符串不能超過{0}個字符!",
"stringLengthInvalid":"此字符串長度必須為{0}個字符!",
"stringStartsWith":"此字符串必須以{0}開頭!",
"stringEndsWith":"此字符串必須以{0}結尾!",
"stringContains":"此字符串必須包含{0}!",
"stringChoices":"此字符串隻能是以下值之一:{0}!",
"stringRegex":"此字符串無效!",
"stringInvertedContains":"此字符串不能包含 {0}!",
"stringLowercase":"此字符串必須全部使用小寫字母!",
"stringUppercase":"此字符串必須全部使用大寫字母!",
"stringSpecialCharacters":"此字符串不能包含任何特殊字符!(僅允許 a-z、0-9 和空格)",
"stringNoSpaces":"此字符串不能包含空格!",
"stringCapitalWord":"建議此字符串中的每個單詞都以大寫字母開頭!",
"stringCapitalSentence":"此字符串中的某些句子似乎冇有以大寫字母開頭!",
"stringPunctuation":"此字符串中的句子似乎冇有以標點符號結尾!",
"numberTooShort":"此數字不能少於{0}位!",
"numberTooLong":"此數字不能超過{0}位!",
"numberLengthInvalid":"此數字長度必須為{0}位!",
"numberTooSmall":"此數字至少為{0}!",
"numberTooLarge":"此數字最多為{0}!",
"numberNotEqual":"此數字必須為{0}!",
"numberStep":"此數字必須是{0}的倍數!",
"numberStepOffset":"此數字必須是{0}的倍數,起始值為{1}!",
"numberStartsWith":"此數字必須以{0}開頭!",
"numberEndsWith":"此數字必須以{0}結尾!",
"numberContains":"此數字必須包含{0}!",
"numberChoices":"此數字隻能是以下值之一:{0}!",
"numberFloat":"此數字不能為小數!",
"numberNegative":"此數字不能為負數!",
"numberPositive":"此數字不能為正數!",
"numberZero":"此數字不能為零!",
"numberNan":"此數字不能為 NaN(非數字)!",
"numberInvertedContains":"此數字不能包含 {0}!",
"booleanTrue":"此佈爾值不能為真!",
"booleanFalse":"此佈爾值不能為假!",
"arrayEmptyDisabled":"此數組不允許為空!",
"arrayEmptyRequired":"此數組必須為空!",
"arrayTooShort":"此數組長度至少為{0}!",
"arrayTooLong":"此數組長度最多為{0}!",
"arrayLengthInvalid":"此數組長度必須為{0}!",
"arrayInvalidTypes":"此數組隻能包含以下類型:{0}!",
"arrayDouble":"此數組不允許重複值!",
"discordInvalidId":"這是無效的Discord {0} ID!",
"discordInvalidIdOptions":"這是無效的Discord {0} ID!您還可以使用以下之一:{1}!",
"discordInvalidToken":"這是無效的Discord令牌(文法上)!",
"colorInvalid":"這是無效的十六進製顔色!",
"emojiTooShort":"此字符串至少需要{0}個錶情符號!",
"emojiTooLong":"此字符串最多隻能有{0}個錶情符號!",
"emojiCustom":"此錶情符號不能是自定義Discord錶情符號!",
"emojiInvalid":"這是無效的錶情符號!",
"urlInvalid":"此URL無效!",
"urlInvalidHttp":"This url can only use the https:// protocol!",
"urlInvalidProtocol":"This url can only use the http:// & https:// protocols!",
"urlInvalidHostname":"此URL的主機名不被允許!",
"urlInvalidExtension":"此URL的擴展名無效!請選擇:{0}!",
"urlInvalidPath":"此URL的路徑無效!",
"idNotUnique":"此ID不唯一,請使用其他ID!",
"idNonExistent":"ID {0}不存在!",
"invalidType":"此屬性必須為類型:{0}!",
"propertyMissing":"此對象缺少屬性{0}!",
"propertyOptional":"此對象中的屬性{0}是可選的!",
"objectDisabled":"此對象已禁用,請使用{0}啟用!",
"nullInvalid":"此屬性不能為null!",
"switchInvalidType":"此屬性必須是以下類型之一:{0}!",
"objectSwitchInvalid":"此對象必須是以下類型之一:{0}!",
"invalidLanguage":"這是無效的語言!",
"invalidButton":"此按鈕必須至少有一個{0}或{1}!",
"unusedOption":"選項{0}未在任何地方使用!",
"unusedQuestion":"問題{0}未在任何地方使用!",
"dropdownOption":"啟用下拉菜單的麵闆隻能包含“ticket”類型的選項!",
"customInvalidVersion":"配置文件中指定的版本不匹配!請確保您已將配置更新至最新版本!"
}
},
"actions":{
"buttons":{
"create":"訪問工單",
"close":"關閉工單",
"delete":"刪除工單",
"reopen":"重新開啟工單",
"claim":"認領工單",
"unclaim":"取消認領工單",
"pin":"置頂工單",
"unpin":"取消置頂工單",
"clear":"刪除工單",
"helpSwitchSlash":"檢視斜杠命令",
"helpSwitchText":"檢視文本命令",
"helpPage":"第{0}頁",
"withReason":"帶原因",
"withoutTranscript":"不帶記錄"
},
"titles":{
"created":"工單已創建",
"close":"工單已關閉",
"delete":"工單已刪除",
"reopen":"工單已重新開啟",
"claim":"工單已認領",
"unclaim":"工單已取消認領",
"pin":"工單已置頂",
"unpin":"工單已取消置頂",
"rename":"工單已重命名",
"move":"工單已移動",
"add":"工單用戶已添加",
"remove":"工單用戶已移除",
"help":"可用命令",
"statsReset":"重置統計",
"blacklistAdd":"用戶已拉黑",
"blacklistRemove":"用戶已解禁",
"blacklistGet":"已拉黑用戶",
"blacklistView":"當前黑名單",
"blacklistAddDm":"已加入黑名單",
"blacklistRemoveDm":"已從黑名單移除",
"clear":"工單已清除",
"clearTickets":"清除工單",
"roles":"角色已更新",
"autoclose":"工單自動關閉",
"autocloseEnabled":"自動關閉已啟用",
"autocloseDisabled":"自動關閉已禁用",
"autodelete":"工單自動刪除",
"autodeleteEnabled":"自動刪除已啟用",
"autodeleteDisabled":"自動刪除已禁用",
"topicSet":"主題已更改",
"prioritySet":"優先級已更改",
"priorityGet":"工單優先級",
"transfer":"工單已轉移"
},
"descriptions":{
"create":"您的工單已創建。點選下方按鈕訪問!",
"close":"工單已成功關閉!",
"delete":"工單已成功刪除!",
"reopen":"工單已成功重新開啟!",
"claim":"工單已成功認領!",
"unclaim":"工單已成功取消認領!",
"pin":"工單已成功置頂!",
"unpin":"工單已成功取消置頂!",
"rename":"工單已成功重命名為{0}!",
"move":"工單已成功移動到{0}!",
"add":"{0}已成功添加到工單!",
"remove":"{0}已成功從工單移除!",
"helpExplanation":"`<名稱>` => 必填參數\n`[名稱]` => 可選參數",
"statsReset":"機器人統計已成功重置!",
"statsError":"無法檢視工單統計!\n{0}不是工單!",
"blacklistAdd":"{0}已成功拉黑!",
"blacklistRemove":"{0}已成功解禁!",
"blacklistGetSuccess":"{0}當前已被拉黑!",
"blacklistGetEmpty":"{0}當前未被拉黑!",
"blacklistViewEmpty":"尚未有人被拉黑!",
"blacklistViewTip":"使用“/blacklist add”拉黑用戶!",
"clearVerify":"確定要刪除多個工單嗎?\n此操作無法撤銷!",
"clearReady":"{0}個工單已成功刪除!",
"rolesEmpty":"未更新任何角色!",
"autocloseLeave":"此工單已自動關閉,因為創建者離開了服務器!",
"autocloseTimeout":"此工單已自動關閉,因為它已超過`{0}小時`未活動!",
"autodeleteLeave":"此工單已自動刪除,因為創建者離開了服務器!",
"autodeleteTimeout":"此工單已自動刪除,因為它已超過`{0}天`未活動!",
"autocloseEnabled":"此工單已啟用自動關閉!\n超過`{0}小時`未活動後將自動關閉!",
"autocloseDisabled":"此工單已禁用自動關閉!\n不再自動關閉!",
"autodeleteEnabled":"此工單已啟用自動刪除!\n超過`{0}天`未活動後將自動刪除!",
"autodeleteDisabled":"此工單已禁用自動刪除!\n不再自動刪除!",
"ticketMessageLimit":"您隻能同時創建{0}個工單!",
"ticketMessageAutoclose":"此工單將在超過{0}小時未活動後自動關閉!",
"ticketMessageAutodelete":"此工單將在超過{0}天未活動後自動刪除!",
"panelReady":"麵闆已在後續消息中可用!\n此消息現在可以刪除!",
"topicSet":"頻道主題已由 {0} 成功更改!",
"prioritySet":"工單優先級已由 {1} 成功更改為 {0}!",
"priorityGet":"此工單當前的優先級為 {0}。",
"transfer":"工單所有權已由 {2} 成功從 {0} 轉移至 {1}!"
},
"modal":{
"closePlaceholder":"您為什麼關閉此工單?",
"deletePlaceholder":"您為什麼刪除此工單?",
"reopenPlaceholder":"您為什麼重新開啟此工單?",
"claimPlaceholder":"您為什麼認領此工單?",
"unclaimPlaceholder":"您為什麼取消認領此工單?",
"pinPlaceholder":"您為什麼置頂此工單?",
"unpinPlaceholder":"您為什麼取消置頂此工單?"
},
"logs":{
"createLog":"{0}創建了一個新工單!",
"closeLog":"此工單已被{0}關閉!",
"closeDm":"您的工單已在我們的服務器中關閉!",
"deleteLog":"此工單已被{0}刪除!",
"deleteDm":"您的工單已在我們的服務器中刪除!",
"reopenLog":"此工單已被{0}重新開啟!",
"reopenDm":"您的工單已在我們的服務器中重新開啟!",
"claimLog":"此工單已被{0}認領!",
"claimDm":"您的工單已在我們的服務器中被認領!",
"unclaimLog":"此工單已被{0}取消認領!",
"unclaimDm":"您的工單已在我們的服務器中取消認領!",
"pinLog":"此工單已被{0}置頂!",
"pinDm":"您的工單已在我們的服務器中置頂!",
"unpinLog":"此工單已被{0}取消置頂!",
"unpinDm":"您的工單已在我們的服務器中取消置頂!",
"renameLog":"此工單已被{1}重命名為{0}!",
"renameDm":"您的工單已在我們的服務器中重命名為{0}!",
"moveLog":"此工單已被{1}移動到{0}!",
"moveDm":"您的工單已在我們的服務器中移動到{0}!",
"addLog":"{0}已被{1}添加到此工單!",
"addDm":"{0}已被添加到您的工單中!",
"removeLog":"{0}已被{1}從此工單移除!",
"removeDm":"{0}已被從您的工單中移除!",
"blacklistAddLog":"{0}被{1}拉黑!",
"blacklistRemoveLog":"{0}被{1}從黑名單移除!",
"blacklistAddDm":"您已被我們的服務器拉黑!\n從現在起,您無法創建工單!",
"blacklistRemoveDm":"您已被我們的服務器解禁!\n現在您可以再次創建工單!",
"clearLog":"{0}個工單已被{1}刪除!",
"transferLog":"此工單的所有權已由 {2} 從 {0} 轉移至 {1}!",
"transferDm":"您在服務器中的工單所有權已從 {0} 轉移至 {1}!",
"prioritySetLog":"此工單的優先級已由 {1} 更改為 {0}!",
"prioritySetDm":"您在服務器中的工單優先級已更改為 {0}!",
"roleUpdateLog":"{0} 已更新其角色!",
"roleUpdateDm":"您在服務器中的角色已更新!"
}
},
"transcripts":{
"success":{
"visit":"檢視記錄",
"ready":"記錄已創建",
"textFileDescription":"這是已刪除工單的文本記錄!",
"htmlProgress":"請等待此HTML記錄處理完成...",
"createdChannel":"服務器中已創建一個新的{0}記錄!",
"createdCreator":"已為您的工單創建一個新的{0}記錄!",
"createdParticipant":"您參與的工單中已創建一個新的{0}記錄!",
"createdActiveAdmin":"您作為管理員參與的工單中已創建一個新的{0}記錄!",
"createdEveryAdmin":"您曾擔任管理員的工單中已創建一個新的{0}記錄!",
"createdOther":"已創建一個新的{0}記錄!"
},
"errors":{
"retry":"重試",
"continue":"刪除無記錄",
"backup":"創建備份記錄",
"error":"創建記錄時出錯。\n您想怎麼做?\n\n在您點選以下按鈕之前,此工單不會被刪除。",
"title":"記錄錯誤"
},
"text":{
"messagesTitle":"消息",
"embedTitle":"EMBED",
"fileTitle":"文件",
"fieldsTitle":"字段",
"reactionsTitle":"反應",
"statsTitle":"統計",
"emptyContent":"<內容為空>",
"noTitle":"<無標題>",
"noDesc":"<無描述>"
}
},
"errors":{
"titles":{
"internalError":"內部錯誤",
"optionMissing":"命令選項缺失",
"optionInvalid":"命令選項無效",
"unknownCommand":"未知命令",
"noPermissions":"無權限",
"unknownTicket":"未知工單",
"deprecatedTicket":"過時工單",
"unknownOption":"未知選項",
"unknownPanel":"未知麵闆",
"notInGuild":"不在服務器中",
"channelRename":"無法重命名頻道",
"busy":"工單繁忙",
"permissionError":"權限錯誤"
},
"descriptions":{
"askForInfo":"請聯係此機器人的所有者獲取更多信息!",
"askForInfoResolve":"如果此問題在多次嘗試後仍未解決,請聯係此機器人的所有者。",
"internalError":"由於內部錯誤,無法回響此{0}!",
"optionMissing":"此命令缺少必填參數!",
"optionInvalid":"此命令中的參數無效!",
"optionInvalidChoose":"請選擇",
"unknownCommand":"請訪問幫助菜單獲取更多信息!",
"noPermissions":"您無權使用此{0}!",
"noPermissionsList":"所需權限:(其中之一)",
"noPermissionsCooldown":"您因冷卻時間無法使用此{0}!",
"noPermissionsBlacklist":"您因被拉黑無法使用此{0}!",
"noPermissionsLimitGlobal":"您無法創建工單,因為服務器已達到最大工單限製!",
"noPermissionsLimitGlobalUser":"您無法創建工單,因為您已達到最大工單限製!",
"noPermissionsLimitOption":"您無法創建工單,因為服務器已達到此選項的最大工單限製!",
"noPermissionsLimitOptionUser":"您無法創建工單,因為您已達到此選項的最大工單限製!",
"unknownTicket":"請在有效工單中重試此命令!",
"deprecatedTicket":"當前頻道不是有效工單!可能是舊版Open Ticket的工單!",
"notInGuild":"此{0}在私信中無效!請在服務器中重試!",
"channelRename":"由於Discord速率限製,機器人目前無法重命名頻道。如果機器人未重啟,頻道將在10分鍾後自動重命名。",
"channelRenameSource":"此錯誤的來源是:{0}",
"busy":"無法使用此{0}!\n工單當前正在被機器人處理。\n\n請幾秒後重試!",
"closeBeforeMessage":"用戶發送消息之前,無法關閉或刪除此工單。",
"closeBeforeAdminMessage":"工單管理員或支援成員發送消息之前,無法關閉或刪除此工單。",
"unableToCreateTicket":"您無法創建工單。"
},
"optionInvalidReasons":{
"stringRegex":"值不符合模式!",
"stringMinLength":"值至少需要{0}個字符!",
"stringMaxLength":"值最多需要{0}個字符!",
"numberInvalid":"無效數字!",
"numberMin":"數字至少為{0}!",
"numberMax":"數字最多為{0}!",
"numberDecimal":"數字不能為小數!",
"numberNegative":"數字不能為負數!",
"numberPositive":"數字不能為正數!",
"numberZero":"數字不能為零!",
"channelNotFound":"無法找到頻道!",
"userNotFound":"無法找到用戶!",
"roleNotFound":"無法找到角色!",
"memberNotFound":"無法找到用戶!",
"mentionableNotFound":"無法找到用戶或角色!",
"channelType":"無效的頻道類型!",
"notInGuild":"此選項需要您在服務器中!"
},
"permissions":{
"developer":"您需要是機器人的開發者。",
"owner":"您需要是服務器所有者。",
"admin":"您需要是服務器管理員。",
"moderator":"您需要是版主。",
"support":"您需要是支援團隊成員。",
"member":"您需要是成員。",
"discord-administrator":"您需要擁有`ADMINISTRATOR`權限。"
},
"actionInvalid":{
"close":"工單已關閉!",
"reopen":"工單未關閉!",
"claim":"工單已認領!",
"unclaim":"工單未認領!",
"pin":"工單已置頂!",
"unpin":"工單未置頂!",
"add":"此用戶已可訪問工單!",
"remove":"無法從此工單移除此用戶!"
}
},
"params":{
"uppercase":{
"ticket":"工單",
"tickets":"工單",
"reason":"原因",
"creator":"創建者",
"remaining":"剩餘時間",
"added":"已添加",
"removed":"已移除",
"filter":"篩選",
"method":"方式",
"type":"類型",
"blacklisted":"已拉黑",
"panel":"麵闆",
"command":"命令",
"system":"係統",
"true":"是",
"false":"否",
"syntax":"文法",
"originalName":"原名稱",
"newName":"新名稱",
"until":"直到",
"validOptions":"有效選項",
"validPanels":"有效麵闆",
"autoclose":"自動關閉",
"autodelete":"自動刪除",
"startupDate":"啟動日期",
"version":"版本",
"name":"名稱",
"role":"角色",
"status":"狀態",
"claimed":"已認領",
"pinned":"已置頂",
"creationDate":"創建日期",
"noone":"無人",
"open":"開啟",
"closed":"關閉",
"priority":"優先級",
"participants":"參與者",
"yes":"是",
"no":"否",
"option":"選項",
"topic":"主題",
"uptime":"係統運行時間",
"messages":"消息",
"embeds":"嵌入",
"files":"文件",
"components":"組件",
"cooldown":"冷卻時間",
"maxTickets":"最大工單數",
"admins":"管理員",
"roles":"角色",
"size":"大小"
},
"lowercase":{
"text":"文本",
"html":"HTML",
"command":"命令",
"modal":"模態框",
"button":"按鈕",
"dropdown":"下拉菜單",
"method":"方式"
}
},
"commands":{
"reason":"指定一個可選原因,該原因將在日誌中可見。",
"help":"獲取所有可用命令的列錶。",
"panel":"生成帶有下拉菜單或按鈕的消息(用於工單創建)。",
"panelId":"您想要生成的麵闆的標識符。",
"panelAutoUpdate":"您希望此麵闆在編輯時自動更新嗎?",
"ticket":"立即創建一個工單。",
"ticketId":"您想要創建的工單的標識符。",
"close":"關閉一個工單。",
"delete":"刪除一個工單。",
"deleteNoTranscript":"刪除此工單而不創建記錄。",
"reopen":"重新開啟一個工單。",
"claim":"認領一個工單。",
"claimUser":"將此工單認領給其他人而不是您自己。",
"unclaim":"取消認領一個工單。",
"pin":"置頂一個工單。",
"unpin":"取消置頂一個工單。",
"move":"移動一個工單。",
"moveId":"您想要移動到的選項的標識符。",
"rename":"重命名一個工單。",
"renameName":"此工單的新名稱。",
"add":"將用戶添加到工單。",
"addUser":"要添加的用戶。",
"remove":"從工單中移除用戶。",
"removeUser":"要移除的用戶。",
"blacklist":"管理工單黑名單。",
"blacklistView":"檢視當前黑名單的列錶。",
"blacklistAdd":"將用戶添加到黑名單。",
"blacklistRemove":"從黑名單中移除用戶。",
"blacklistGet":"獲取被拉黑用戶的詳細信息。",
"blacklistGetUser":"要獲取詳細信息的用戶。",
"stats":"檢視機器人、成員或工單的統計信息。",
"statsReset":"重置機器人的所有統計信息(並從零開始計數)。",
"statsGlobal":"檢視全局統計信息。",
"statsUser":"檢視服務器中用戶的統計信息。",
"statsUserUser":"要檢視的用戶。",
"statsTicket":"檢視服務器中工單的統計信息。",
"statsTicketTicket":"要檢視的工單。",
"clear":"同時刪除多個工單。",
"clearFilter":"清除工單的篩選條件。",
"clearFilters":{
"all":"全部",
"open":"開啟",
"close":"關閉",
"claim":"已認領",
"unclaim":"未認領",
"pin":"已置頂",
"unpin":"未置頂",
"autoclose":"自動關閉"
},
"autoclose":"管理工單中的自動關閉。",
"autocloseDisable":"禁用此工單的自動關閉。",
"autocloseEnable":"啟用此工單的自動關閉。",
"autocloseEnableTime":"工單需要多少小時不活動才能自動關閉。",
"autodelete":"管理工單中的自動刪除。",
"autodeleteDisable":"禁用此工單的自動刪除。",
"autodeleteEnable":"啟用此工單的自動刪除。",
"autodeleteEnableTime":"工單需要多少天不活動才能自動刪除。",
"topic":"管理工單頻道的主題。",
"topicSet":"設定工單頻道的主題。",
"topicValue":"頻道的新主題。",
"topicList":"獲取所有工單及其主題和統計信息的列錶。",
"priority":"管理工單的優先級。",
"prioritySet":"設定工單的優先級。",
"priorityValue":"頻道的優先級。",
"priorityGet":"獲取工單的優先級。",
"priorityList":"獲取所有工單的優先級狀態列錶。",
"transfer":"將工單所有權從一位用戶轉移至另一位用戶。",
"transferUser":"要轉移給的用戶。"
},
"helpMenu":{
"help":"獲取所有可用命令的列錶。",
"ticket":"立即創建一個工單。",
"close":"關閉一個工單,這將禁用此頻道的寫入權限。",
"delete":"刪除一個工單,啟用時會創建記錄。",
"reopen":"重新開啟一個工單,這將重新啟用此頻道的寫入權限。",
"pin":"置頂一個工單。這將把工單移動到頂部,並在名稱前添加'📌'錶情符號。",
"unpin":"取消置頂一個工單。工單將保持當前位置,但會移除'📌'錶情符號。",
"move":"移動一個工單。這將更改此工單的類型。",
"rename":"重命名一個工單。這將更改此工單的頻道名稱。",
"claim":"認領一個工單。通過此操作,您可以告知團隊您正在處理此工單。",
"unclaim":"取消認領一個工單。通過此操作,您可以告知團隊此工單已空閒。",
"add":"將用戶添加到工單。這將允許用戶在此工單中讀取和寫入。",
"remove":"從工單中移除用戶。這將移除用戶在此工單中讀取和寫入的權限。",
"panel":"生成帶有下拉菜單或按鈕的消息(用於工單創建)。",
"blacklistView":"檢視當前黑名單的列錶。",
"blacklistAdd":"將用戶添加到黑名單。",
"blacklistRemove":"從黑名單中移除用戶。",
"blacklistGet":"獲取被拉黑用戶的詳細信息。",
"statsGlobal":"檢視全局統計信息。",
"statsTicket":"檢視服務器中工單的統計信息。",
"statsUser":"檢視服務器中用戶的統計信息。",
"statsReset":"重置機器人的所有統計信息(並從零開始計數)。",
"autocloseDisable":"禁用此工單的自動關閉。",
"autocloseEnable":"啟用此工單的自動關閉。",
"autodeleteDisable":"禁用此工單的自動刪除。",
"autodeleteEnable":"啟用此工單的自動刪除。",
"categories":{
"general":"通用命令",
"basicTicket":"基礎工單命令",
"advancedTicket":"高級工單命令",
"userTicket":"用戶工單命令",
"admin":"管理員命令",
"advanced":"高級命令",
"extra":"額外命令"
}
},
"stats":{
"scopes":{
"global":"全局統計",
"system":"係統統計",
"user":"用戶統計",
"ticket":"工單統計",
"participants":"參與者",
"messages":"消息"
},
"properties":{
"ticketsCreated":"工單已創建",
"ticketsClosed":"工單已關閉",
"ticketsDeleted":"工單已刪除",
"ticketsReopened":"工單已重新開啟",
"ticketsAutoclosed":"工單已自動關閉",
"ticketsClaimed":"工單已認領",
"ticketsPinned":"工單已置頂",
"ticketsMoved":"工單已移動",
"usersBlacklisted":"用戶已拉黑",
"transcriptsCreated":"記錄已創建",
"ticketsAutodeleted":"自動刪除的工單",
"ticketsTransferred":"已轉移的工單",
"ticketVolume":"工單量",
"averageTickets":"平均工單數/用戶",
"currentTickets":"當前工單",
"age":"工單時長",
"responseTime":"回響時間",
"resolutionTime":"解決時間",
"createdOn":"創建於",
"createdBy":"創建者",
"closedOn":"關閉於",
"closedBy":"關閉者",
"claimedOn":"認領於",
"claimedBy":"認領者",
"pinnedOn":"固定於",
"pinnedBy":"固定者",
"deletedOn":"刪除於",
"deletedBy":"刪除者"
},
"roles":{
"developer":"開發者",
"serverOwner":"服務器所有者",
"serverAdmin":"服務器管理員",
"moderator":"管理團隊",
"support":"支援團隊",
"member":"成員"
}
},
"panel":{
"selectTicket":"選擇您的工單",
"selectRole":"選擇您的角色",
"selectOption":"選擇您的選項"
},
"priorities":{
"urgent":"緊急",
"veryHigh":"非常高",
"high":"高",
"normal":"普通",
"low":"低",
"veryLow":"非常低",
"none":"無"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["palestinian"],
"lastedited":"16/02/2026",
"language":"Turkish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["Anderskiy"],
"lastedited":"16/02/2026",
"language":"Ukrainian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.1.3",
"otversion":"v4.2.0",
"translators":["ngocdiep2006"],
"lastedited":"16/02/2026",
"language":"Vietnamese",
+10 -9
View File
@@ -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",
@@ -19,20 +19,21 @@
"test": "node index.js --dev-config --dev-database --soft-plugins",
"testsetup": "node index.js --cli --dev-config --dev-database --soft-plugins",
"testnc": "node index.js --no-compile --dev-config --dev-database --soft-plugins",
"docs": "npx typedoc --options .docs/typedoc-config.json && node .docs/createDocs.js",
"mergelang": "node .docs/mergeTranslations.js"
"docs": "npx typedoc --options .tools/typedoc-config.json && node .tools/createDocs.js",
"mergelang": "node .tools/mergeTranslations.js"
},
"type": "commonjs",
"type": "module",
"license": "GPL-3.0-only",
"dependencies": {
"@discordjs/rest": "^2.6.0",
"@discordjs/rest": "^2.6.1",
"@open-discord-bots/framework": "^0.4.4",
"@types/node": "^22.5.0",
"@types/terminal-kit": "^2.5.7",
"ansis": "^4.2.0",
"discord.js": "^14.24.2",
"formatted-json-stringify": "^1.2.1",
"discord.js": "^14.26.4",
"formatted-json-stringify": "^1.3.2",
"terminal-kit": "^3.1.2",
"typescript": "^5.9.3"
"typescript": "^6.0.3"
},
"repository": {
"type": "git",
@@ -45,7 +46,7 @@
"homepage": "https://openticket.dj-dj.be",
"imports": {
"#opendiscord": "./dist/src/index.js",
"#opendiscord-types": "./dist/src/core/api/api.js"
"#opendiscord-types": "./dist/src/core/api.js"
},
"funding": {
"type": "individual",
+5 -5
View File
@@ -6,15 +6,15 @@ import * as discord from "discord.js"
//// Enable it in the plugin.json file! ////
/////////////////////////////////////////////
if (utilities.project != "openticket") throw new api.ODPluginError("This plugin only works in Open Ticket!")
if (opendiscord.project != "openticket") throw new api.ODPluginError("This plugin only works in Open Ticket!")
//Add Typescript autocomplete support for plugin data. (!!!OPTIONAL!!!)
declare module "#opendiscord-types" {
export interface ODPluginManagerIds_Default {
export interface ODPluginManagerIdMappings {
"example-plugin":api.ODPlugin
}
export interface ODConfigManagerIds_Default {
"example-plugin:config":api.ODJsonConfig
export interface ODConfigManagerIdMappings {
"example-plugin:config":api.ODJsonConfig<{testVariable1:boolean,testVariable2:number,testVariable3:string}>
}
}
@@ -30,7 +30,7 @@ opendiscord.events.get("onConfigLoad").listen((configManager) => {
//Let's also log it to the console to let us know it worked!
const ourConfig = configManager.get("example-plugin:config")
opendiscord.log("The example config loaded successfully!","plugin",[
{key:"var-1",value:ourConfig.data.testVariable1},
{key:"var-1",value:ourConfig.data.testVariable1.toString()},
{key:"var-2",value:ourConfig.data.testVariable2.toString()},
{key:"var-3",value:ourConfig.data.testVariable3.toString()}
])
+12 -25
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//TICKET ADD USER SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:add-ticket-user"))
opendiscord.actions.get("opendiscord:add-ticket-user").workers.add([
new api.ODWorker("opendiscord:add-ticket-user",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:add-ticket-user",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason,data} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to add user to ticket! Open Ticket doesn't support threads!")
@@ -34,44 +34,31 @@ export const registerActions = async () => {
opendiscord.log("Failed to add channel permission overwrites on add-ticket-user","error")
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket user adding!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value,hidden:true}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:add-message").build(source,{guild,channel,user,ticket,reason,data})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:add-message").build(origin,{guild,channel,user,ticket,reason,data})).message)
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketUserAdded").emit([ticket,user,data,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"add",reason,additionalData:data}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,data} = params
opendiscord.log(user.displayName+" added "+data.displayName+" to a ticket!","info",[
@@ -80,7 +67,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+129
View File
@@ -0,0 +1,129 @@
///////////////////////////////////////
//CALCULATE TICKET CATEGORY SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:calculate-ticket-category"))
opendiscord.actions.get("opendiscord:calculate-ticket-category").workers.add([
new api.ODWorker("opendiscord:default-category",2,async (instance,params,origin,cancel) => {
//handle default category
const {guild,user,channel,option,ticket,currentCategoryId} = params
const defaultCategoryId = option.get("opendiscord:channel-category").value
if (!defaultCategoryId){
//default category is disabled
instance.newCategoryId = null
instance.newCategoryMode = null
instance.newCategory = null
instance.shouldChangeCategory = (instance.newCategoryId !== currentCategoryId)
}else{
const defaultCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,defaultCategoryId)
if (defaultCategory){
//default category is enabled
instance.newCategoryId = defaultCategoryId
instance.newCategoryMode = "default"
instance.newCategory = defaultCategory
instance.shouldChangeCategory = (instance.newCategoryId !== currentCategoryId)
}else{
//default category is not found (do not switch categories)
opendiscord.log("Unable to find ticket category '"+defaultCategoryId+"' #1","error",[
{key:"categoryid",value:defaultCategoryId},
{key:"type",value:"default"}
])
instance.newCategoryId = null
instance.newCategoryMode = null
instance.newCategory = null
instance.shouldChangeCategory = false
}
}
}),
new api.ODWorker("opendiscord:close-category",1,async (instance,params,origin,cancel) => {
//handle close category
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 = generalConfig.data.ticketSystem.closedCategory.categoryId
if (!closeCategoryId) return
const closeCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,closeCategoryId)
if (closeCategory){
//close category is enabled
instance.newCategoryId = closeCategoryId
instance.newCategoryMode = "close"
instance.newCategory = closeCategory
instance.shouldChangeCategory = (instance.newCategoryId !== currentCategoryId)
}else{
//close category is not found (do not switch categories)
opendiscord.log("Unable to find ticket category '"+closeCategoryId+"' #2","error",[
{key:"categoryid",value:closeCategoryId},
{key:"type",value:"close"}
])
instance.newCategoryId = null
instance.newCategoryMode = null
instance.newCategory = null
instance.shouldChangeCategory = false
}
}),
new api.ODWorker("opendiscord:claim-category",0,async (instance,params,origin,cancel) => {
//handle claim category
const {guild,user,channel,option,ticket,currentCategoryId} = params
if (!ticket) return
if (!ticket.get("opendiscord:claimed").value) return
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)
if (claimCategory){
//claim category is enabled
instance.newCategoryId = claimCategoryId
instance.newCategoryMode = "claim"
instance.newCategory = claimCategory
instance.shouldChangeCategory = (instance.newCategoryId !== currentCategoryId)
}else{
//claim category is not found (do not switch categories)
opendiscord.log("Unable to find ticket category '"+claimCategoryId+"' #3","error",[
{key:"categoryid",value:claimCategoryId},
{key:"type",value:"claim"}
])
instance.newCategoryId = null
instance.newCategoryMode = null
instance.newCategory = null
instance.shouldChangeCategory = false
}
}),
new api.ODWorker("opendiscord:backup-category",-100,async (instance,params,origin,cancel) => {
//handle backup category
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 = generalConfig.data.ticketSystem.backupCategory.categoryId
if (!backupCategoryId) return
const backupCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,backupCategoryId)
if (backupCategory){
//backup category is enabled
instance.newCategoryId = backupCategoryId
instance.newCategoryMode = "backup"
instance.newCategory = backupCategory
instance.shouldChangeCategory = (instance.newCategoryId !== currentCategoryId)
}else{
//backup category is not found (do not switch categories)
opendiscord.log("Unable to find ticket category '"+backupCategoryId+"' #4","error",[
{key:"categoryid",value:backupCategoryId},
{key:"type",value:"backup"}
])
instance.newCategoryId = null
instance.newCategoryMode = null
instance.newCategory = null
instance.shouldChangeCategory = false
}
})
])
}
+30
View File
@@ -0,0 +1,30 @@
///////////////////////////////////////
//CALCULATE TICKET NAME SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:calculate-ticket-name"))
opendiscord.actions.get("opendiscord:calculate-ticket-name").workers.add([
new api.ODWorker("opendiscord:calculate-ticket-name",0,async (instance,params,origin,cancel) => {
const {guild,user,channel,option,ticket,currentChannelName} = params
//calculate base channel name
const channelPrefix = option.get("opendiscord:channel-prefix").value
const channelSuffix = (ticket) ? ticket.get("opendiscord:channel-suffix").value : (await opendiscord.options.suffix.getSuffixFromOption(option,user,guild) ?? "unknown")
const channelRenamed = (ticket && ticket.exists("opendiscord:channel-renamed")) ? ticket.get("opendiscord:channel-renamed").value : null
const baseChannelName = (channelRenamed) ? channelRenamed : channelPrefix+channelSuffix
//calculate status emojis
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
instance.newChannelSuffix = channelSuffix
instance.shouldChangeName = (instance.newChannelName !== currentChannelName)
})
])
}
+39 -219
View File
@@ -1,15 +1,16 @@
///////////////////////////////////////
//TICKET CLAIMING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:claim-ticket"))
opendiscord.actions.get("opendiscord:claim-ticket").workers.add([
new api.ODWorker("opendiscord:claim-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:claim-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to claim ticket! Open Ticket doesn't support threads!")
@@ -22,67 +23,66 @@ export const registerActions = async () => {
ticket.get("opendiscord:busy").value = true
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-claimed",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-claimed",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-claimed",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-claimed",user.id,1,"increase")
//update category
//calculate & update category
if (typeof params.allowCategoryChange == "boolean" ? params.allowCategoryChange : true){
const rawClaimCategory = ticket.option.get("opendiscord:channel-categories-claimed").value.find((c) => c.user == user.id)
const claimCategory = (rawClaimCategory) ? rawClaimCategory.category : null
if (claimCategory){
try {
channel.setParent(claimCategory,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = "claimed"
ticket.get("opendiscord:category").value = claimCategory
}catch(e){
opendiscord.log("Unable to move ticket to 'claimed category'!","error",[
const categoryResult = await opendiscord.actions.get("opendiscord:calculate-ticket-category").run("claim-ticket",{guild,user,option:ticket.option,channel,ticket,currentCategoryId:channel.parentId})
if (categoryResult && categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryId !== "undefined" && typeof categoryResult.newCategoryMode !== "undefined" && typeof categoryResult.newCategory !== "undefined"){
const originalCategoryName = channel.parent?.name ?? "<unknown>"
const newCategoryName = categoryResult.newCategory?.name ?? "<unknown>"
try{
await utilities.timedAwait(channel.setParent(categoryResult.newCategoryId,{lockPermissions:false}),3000,(err) => {
process.emit("uncaughtException",new Error("Error: Unable to change channel parent: "+err))
})
ticket.get("opendiscord:category-mode").value = categoryResult.newCategoryMode
ticket.get("opendiscord:category").value = categoryResult.newCategoryId
}catch(err){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-category").build("ticket-claim",{guild,channel,user,originalCategory:originalCategoryName,newCategory:newCategoryName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
opendiscord.log("Unable to move ticket to claimed category.","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"categoryid",value:claimCategory}
{key:"categoryid",value:categoryResult.newCategoryId ?? "/"}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket claiming!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:claim-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:claim-message").build(origin,{guild,channel,user,ticket,reason})).message)
if (sentMsg) await interactiveMsgState.setMsgState({channel,message:sentMsg},{
messageType:"claim-message",
messageOrigin:"other",
messageAuthor:user.id,
messageReason:reason
},false)
}
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketClaimed").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"claim",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" claimed a ticket!","info",[
@@ -91,7 +91,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
@@ -99,184 +99,4 @@ export const registerActions = async () => {
//set busy to false in case of crash or cancel
params.ticket.get("opendiscord:busy").value = false
})
}
export const registerVerifyBars = async () => {
//CLAIM TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:claim-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:claim-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.claim
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:claim-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when already claimed
if (ticket.get("opendiscord:claimed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.claim"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start claiming ticket
if (params.data == "reason"){
//claim with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:claim-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//claim without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:claim-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:claim-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//CLAIM TICKET UNCLAIM MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:claim-ticket-unclaim-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-unclaim-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:claim-ticket-unclaim-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.claim
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:claim-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when already claimed
if (ticket.get("opendiscord:claimed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.claim"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start claiming ticket
if (params.data == "reason"){
//claim with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:claim-ticket-reason").build("unclaim-message",{guild,channel,user,ticket}))
}else{
//claim without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:claim-ticket").run("unclaim-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:claim-message").build("unclaim-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:claim-ticket-unclaim-message").failure.add([
new api.ODWorker("opendiscord:back-to-unclaim-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:unclaim-message").build("other",{guild,channel,user,ticket,reason}))
})
])
}
+8 -8
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//CLEAR TICKETS SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:clear-tickets"))
opendiscord.actions.get("opendiscord:clear-tickets").workers.add([
new api.ODWorker("opendiscord:clear-tickets",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:clear-tickets",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,filter,list} = params
await opendiscord.events.get("onTicketsClear").emit([list,user,channel,filter])
@@ -43,21 +43,21 @@ export const registerActions = async () => {
instance.list = nameList
await opendiscord.events.get("afterTicketsCleared").emit([list,user,channel,filter])
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,filter,list:instance.list ?? []}))
if (logChannel) logChannel.send(await opendiscord.builders.messages.getSafe("opendiscord:clear-logs").build(origin,{guild,channel,user,filter,list:instance.list ?? []}))
}
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,user,filter,list} = params
opendiscord.log(user.displayName+" cleared "+list.length+" tickets!","info",[
{key:"user",value:user.username},
{key:"userid",value:user.id,hidden:true},
{key:"method",value:source},
{key:"method",value:origin},
{key:"filter",value:filter}
])
})
+41 -208
View File
@@ -1,16 +1,17 @@
///////////////////////////////////////
//TICKET CLOSING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:close-ticket"))
opendiscord.actions.get("opendiscord:close-ticket").workers.add([
new api.ODWorker("opendiscord:close-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:close-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to close ticket! Open Ticket doesn't support threads!")
@@ -25,29 +26,34 @@ export const registerActions = async () => {
ticket.get("opendiscord:reopened-by").value = null
ticket.get("opendiscord:reopened-on").value = null
if (source == "autoclose") ticket.get("opendiscord:autoclosed").value = true
if (origin == "autoclose") ticket.get("opendiscord:autoclosed").value = true
ticket.get("opendiscord:open").value = false
ticket.get("opendiscord:busy").value = true
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-closed",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-closed",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-closed",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-closed",user.id,1,"increase")
//update category
//calculate & update category
if (typeof params.allowCategoryChange == "boolean" ? params.allowCategoryChange : true){
const closeCategory = ticket.option.get("opendiscord:channel-category-closed").value
if (closeCategory !== ""){
try {
channel.setParent(closeCategory,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = "closed"
ticket.get("opendiscord:category").value = closeCategory
}catch(e){
opendiscord.log("Unable to move ticket to 'closed category'!","error",[
const categoryResult = await opendiscord.actions.get("opendiscord:calculate-ticket-category").run("close-ticket",{guild,user,option:ticket.option,channel,ticket,currentCategoryId:channel.parentId})
if (categoryResult && categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryId !== "undefined" && typeof categoryResult.newCategoryMode !== "undefined" && typeof categoryResult.newCategory !== "undefined"){
const originalCategoryName = channel.parent?.name ?? "<unknown>"
const newCategoryName = categoryResult.newCategory?.name ?? "<unknown>"
try{
await utilities.timedAwait(channel.setParent(categoryResult.newCategoryId,{lockPermissions:false}),3000,(err) => {
process.emit("uncaughtException",new Error("Error: Unable to change channel parent: "+err))
})
ticket.get("opendiscord:category-mode").value = categoryResult.newCategoryMode
ticket.get("opendiscord:category").value = categoryResult.newCategoryId
}catch(err){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-category").build("ticket-close",{guild,channel,user,originalCategory:originalCategoryName,newCategory:newCategoryName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
opendiscord.log("Unable to move ticket to closed category.","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"categoryid",value:closeCategory}
{key:"categoryid",value:categoryResult.newCategoryId ?? "/"}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
}
@@ -93,7 +99,7 @@ export const registerActions = async () => {
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:[],
@@ -109,44 +115,39 @@ export const registerActions = async () => {
})
channel.permissionOverwrites.set(permissions)
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket closing!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:close-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:close-message").build(origin,{guild,channel,user,ticket,reason})).message)
if (sentMsg) await interactiveMsgState.setMsgState({channel,message:sentMsg},{
messageType:"close-message",
messageOrigin:"other",
messageAuthor:user.id,
messageReason:reason
},false)
}
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketClosed").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"close",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" closed a ticket!","info",[
@@ -155,7 +156,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
@@ -163,172 +164,4 @@ export const registerActions = async () => {
//set busy to false in case of crash or cancel
params.ticket.get("opendiscord:busy").value = false
})
}
export const registerVerifyBars = async () => {
//CLOSE TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:close-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:close-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:close-ticket",0,async (instance,params,source,cancel) => {
const {user,member,channel,guild} = instance
//check permissions
const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.close,"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("button",{guild,channel,user,permissions:["support"]}))
return cancel()
}
//check is in guild/server
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
//check if ticket exists
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when already closed
if (ticket.get("opendiscord:closed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:lang.getTranslation("errors.actionInvalid.close"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//return when not allowed because of missing messages
if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
}
//start closing ticket
if (params.data == "reason"){
//close with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:close-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//close without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:close-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:close-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//CLOSE TICKET REOPEN MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:close-ticket-reopen-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-reopen-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:close-ticket-reopen-message").success.add([
new api.ODWorker("opendiscord:close-ticket",0,async (instance,params,source,cancel) => {
const {user,member,channel,guild} = instance
//check permissions
const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.close,"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("button",{guild,channel,user,permissions:["support"]}))
return cancel()
}
//check is in guild/server
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
//check if ticket exists
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when already closed
if (ticket.get("opendiscord:closed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:lang.getTranslation("errors.actionInvalid.close"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//return when not allowed because of missing messages
if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
}
//start closing ticket
if (params.data == "reason"){
//close with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:close-ticket-reason").build("reopen-message",{guild,channel,user,ticket}))
}else{
//close without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:close-ticket").run("reopen-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:close-message").build("reopen-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:close-ticket-reopen-message").failure.add([
new api.ODWorker("opendiscord:back-to-reopen-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build("other",{guild,channel,user,ticket,reason}))
})
])
}
+50 -68
View File
@@ -1,63 +1,36 @@
///////////////////////////////////////
//TICKET CREATION SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:create-ticket"))
opendiscord.actions.get("opendiscord:create-ticket").workers.add([
new api.ODWorker("opendiscord:create-ticket",3,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:create-ticket",3,async (instance,params,origin,cancel) => {
const {guild,user,answers,option} = params
await opendiscord.events.get("onTicketCreate").emit([user])
await opendiscord.events.get("onTicketChannelCreation").emit([option,user])
//get channel properties
const channelPrefix = option.get("opendiscord:channel-prefix").value
const channelCategory = option.get("opendiscord:channel-category").value
const channelBackupCategory = option.get("opendiscord:channel-category-backup").value
const channelTopicText = option.get("opendiscord:channel-topic").value
const channelSuffix = await opendiscord.options.suffix.getSuffixFromOption(option,user,guild)
const channelName = channelPrefix+channelSuffix
//handle category
let category: string|null = null
let categoryMode: "backup"|"normal"|null = null
if (channelCategory != ""){
//category enabled
const normalCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelCategory)
if (!normalCategory){
//default category was not found
opendiscord.log("Ticket Creation Error: Unable to find category! #1","error",[
{key:"categoryid",value:channelCategory},
{key:"backup",value:"false"}
])
}else{
//default category was found
if (normalCategory.children.cache.size >= 50 && channelBackupCategory != ""){
//use backup category
const backupCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelBackupCategory)
if (!backupCategory){
//default category was not found
opendiscord.log("Ticket Creation Error: Unable to find category! #2","error",[
{key:"categoryid",value:channelBackupCategory},
{key:"backup",value:"true"}
])
}else{
category = backupCategory.id
categoryMode = "backup"
}
}else{
//use default category
category = normalCategory.id
categoryMode = "normal"
}
}
}
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("create-ticket",{guild,user,option,channel:null,ticket:null,currentChannelName:null})
if (!channelNameResult) return opendiscord.log("Ticket Creation Error: Unable to calculate ticket name.","error")
const channelName = (channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined") ? channelNameResult.newChannelName : "ot-unnamed-ticket"
const channelSuffix = (typeof channelNameResult.newChannelSuffix !== "undefined") ? channelNameResult.newChannelSuffix : "unknown"
//calculate category
const categoryResult = await opendiscord.actions.get("opendiscord:calculate-ticket-category").run("create-ticket",{guild,user,option,channel:null,ticket:null,currentCategoryId:null})
if (!categoryResult) return opendiscord.log("Ticket Creation Error: Unable to calculate ticket category.","error")
const ticketCategoryId = (categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryId !== "undefined") ? categoryResult.newCategoryId : undefined
const ticketCategoryMode = (categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryMode !== "undefined") ? categoryResult.newCategoryMode : undefined
//handle permissions
const permissions: discord.OverwriteResolvable[] = [{
@@ -118,15 +91,15 @@ export const registerActions = async () => {
//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({
@@ -134,7 +107,7 @@ export const registerActions = async () => {
name:channelName,
nsfw:false,
topic:(channelTopics.length > 0) ? channelTopics.join(" • ") : undefined,
parent:category,
parent:ticketCategoryId,
reason:"Ticket Created By "+user.displayName,
permissionOverwrites:permissions,
rateLimitPerUser:slowMode
@@ -148,6 +121,7 @@ export const registerActions = async () => {
new api.ODTicketData("opendiscord:ticket-message",null),
new api.ODTicketData("opendiscord:participants",participants),
new api.ODTicketData("opendiscord:channel-suffix",channelSuffix),
new api.ODTicketData("opendiscord:channel-renamed",null),
new api.ODTicketData("opendiscord:previous-creators",[]),
new api.ODTicketData("opendiscord:open",true),
@@ -167,8 +141,8 @@ export const registerActions = async () => {
new api.ODTicketData("opendiscord:pinned-on",null),
new api.ODTicketData("opendiscord:for-deletion",false),
new api.ODTicketData("opendiscord:category",category),
new api.ODTicketData("opendiscord:category-mode",categoryMode),
new api.ODTicketData("opendiscord:category",ticketCategoryId ?? null),
new api.ODTicketData("opendiscord:category-mode",ticketCategoryMode ?? null),
new api.ODTicketData("opendiscord:autoclose-enabled",option.get("opendiscord:autoclose-enable-hours").value),
new api.ODTicketData("opendiscord:autoclose-hours",(option.get("opendiscord:autoclose-enable-hours").value ? option.get("opendiscord:autoclose-hours").value : 0)),
@@ -184,8 +158,8 @@ export const registerActions = async () => {
])
//manage stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-created",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-created",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-created",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-created",user.id,1,"increase")
//manage bot permissions
await opendiscord.events.get("onTicketPermissionsCreated").emit([option,opendiscord.permissions,channel,user])
@@ -197,7 +171,7 @@ export const registerActions = async () => {
instance.ticket = ticket
opendiscord.tickets.add(ticket)
}),
new api.ODWorker("opendiscord:send-ticket-message",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:send-ticket-message",2,async (instance,params,origin,cancel) => {
const {guild,user,answers,option} = params
const {ticket,channel} = instance
@@ -207,17 +181,23 @@ export const registerActions = async () => {
//check if ticket message is enabled
if (!option.get("opendiscord:ticket-message-enabled").value) return
try {
const msg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build(source,{guild,channel,user,ticket})).message)
const ticketMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build(origin,{guild,channel,user,ticket})).message)
ticket.get("opendiscord:ticket-message").value = msg.id
if (ticketMsg) await interactiveMsgState.setMsgState({channel,message:ticketMsg},{
messageType:"ticket-message",
messageOrigin:"other",
messageAuthor:user.id
},false)
ticket.get("opendiscord:ticket-message").value = ticketMsg.id
//pin ticket message (if required)
if (generalConfig.data.system.pinFirstTicketMessage && msg.pinnable) await msg.pin("Ticket Message")
if (generalConfig.data.ticketSystem.pinFirstTicketMessage && ticketMsg.pinnable) await ticketMsg.pin("Ticket Message")
//manage stats
await opendiscord.stats.get("opendiscord:ticket").setStat("opendiscord:messages-sent",ticket.id.value,1,"increase")
await opendiscord.statistics.get("opendiscord:ticket").setStat("opendiscord:messages-sent",ticket.id.value,1,"increase")
await opendiscord.events.get("afterTicketMainMessageCreated").emit([ticket,msg,channel,user])
await opendiscord.events.get("afterTicketMainMessageCreated").emit([ticket,ticketMsg,channel,user])
}catch(err){
process.emit("uncaughtException",err)
//something went wrong while sending the ticket message
@@ -225,20 +205,22 @@ export const registerActions = async () => {
}
await opendiscord.events.get("afterTicketCreated").emit([ticket,user,channel])
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
const {guild,user,answers,option} = params
const {ticket,channel} = instance
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(source,{guild,channel,user,ticket}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,user,answers,option} = params
const {ticket,channel} = instance
@@ -249,7 +231,7 @@ export const registerActions = async () => {
{key:"userid",value:user.id,hidden:true},
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"method",value:source},
{key:"method",value:origin},
{key:"option",value:option.id.value}
])
})
+10 -10
View File
@@ -1,13 +1,13 @@
///////////////////////////////////////
//TICKET CREATION SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:create-ticket-permissions"))
opendiscord.actions.get("opendiscord:create-ticket-permissions").workers.add([
new api.ODWorker("opendiscord:check-blacklist",4,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:check-blacklist",4,(instance,params,origin,cancel) => {
if (!params.option.get("opendiscord:allow-blacklisted-users").value && opendiscord.blacklist.exists(params.user.id)){
instance.valid = false
instance.reason = "blacklist"
@@ -19,7 +19,7 @@ export const registerActions = async () => {
return cancel()
}
}),
new api.ODWorker("opendiscord:check-cooldown",3,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:check-cooldown",3,(instance,params,origin,cancel) => {
const cooldown = opendiscord.cooldowns.get("opendiscord:option-cooldown_"+params.option.id.value)
if (cooldown && cooldown instanceof api.ODTimeoutCooldown && cooldown.use(params.user.id)){
instance.valid = false
@@ -36,16 +36,16 @@ export const registerActions = async () => {
return cancel()
}
}),
new api.ODWorker("opendiscord:check-global-limits",2,(instance,params,source,cancel) => {
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 const registerActions = async () => {
{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",[
@@ -67,7 +67,7 @@ export const registerActions = async () => {
return cancel()
}
}),
new api.ODWorker("opendiscord:check-option-limits",1,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:check-option-limits",1,(instance,params,origin,cancel) => {
if (!params.option.exists("opendiscord:limits-enabled") || !params.option.get("opendiscord:limits-enabled").value) return
const allTickets = opendiscord.tickets.getFiltered((ticket) => ticket.option.id.value == params.option.id.value)
@@ -97,7 +97,7 @@ export const registerActions = async () => {
return cancel()
}
}),
new api.ODWorker("opendiscord:valid",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:valid",0,(instance,params,origin,cancel) => {
instance.valid = true
instance.reason = null
cancel()
+40 -14
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//TRANSCRIPT CREATION SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const transcriptConfig = opendiscord.configs.get("opendiscord:transcripts")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:create-transcript"))
opendiscord.actions.get("opendiscord:create-transcript").workers.add([
new api.ODWorker("opendiscord:select-compiler",4,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:select-compiler",4,async (instance,params,origin,cancel) => {
const {channel,user,ticket} = params
if (channel.type != discord.ChannelType.GuildText) return cancel()
if (!transcriptConfig.data.general.enabled) return cancel()
@@ -29,10 +29,15 @@ export const registerActions = async () => {
if (transcriptConfig.data.general.mode == "text") instance.compiler = opendiscord.transcripts.get("opendiscord:text-compiler")
else if (transcriptConfig.data.general.mode == "html") instance.compiler = opendiscord.transcripts.get("opendiscord:html-compiler")
}),
new api.ODWorker("opendiscord:init-transcript",3,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:init-transcript",3,async (instance,params,origin,cancel) => {
const {channel,user,ticket} = params
if (channel.type != discord.ChannelType.GuildText) return cancel()
if (!transcriptConfig.data.general.enabled) return cancel()
if (!instance.compiler){
instance.success = false
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:init-transcript) => Instance is missing transcript compiler!")
}
//run transcript compiler init()
await opendiscord.events.get("onTranscriptInit").emit([opendiscord.transcripts,ticket,channel,user])
@@ -63,7 +68,7 @@ export const registerActions = async () => {
}
await opendiscord.events.get("afterTranscriptInitiated").emit([opendiscord.transcripts,ticket,channel,user])
}),
new api.ODWorker("opendiscord:compile-transcript",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:compile-transcript",2,async (instance,params,origin,cancel) => {
const {channel,user,ticket} = params
if (channel.type != discord.ChannelType.GuildText) return cancel()
if (!instance.compiler){
@@ -71,6 +76,11 @@ export const registerActions = async () => {
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:compile-transcript) => Instance is missing transcript compiler!")
}
if (typeof instance.initData == "undefined"){
instance.success = false
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:compile-transcript) => Instance is missing transcript initData!")
}
//run transcript compiler compile()
await opendiscord.events.get("onTranscriptCompile").emit([opendiscord.transcripts,ticket,channel,user])
@@ -93,15 +103,31 @@ export const registerActions = async () => {
}
await opendiscord.events.get("afterTranscriptCompiled").emit([opendiscord.transcripts,ticket,channel,user])
}),
new api.ODWorker("opendiscord:ready-transcript",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:ready-transcript",1,async (instance,params,origin,cancel) => {
if (!instance.compiler){
instance.success = false
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:ready-transcript) => Instance is missing transcript compiler! (1)")
}
if (!instance.result){
instance.success = false
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:ready-transcript) => Instance is missing transcript result!")
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:ready-transcript) => Instance is missing transcript result! (1)")
}
//run transcript compiler ready()
utilities.runAsync(async () => {
if (!instance.compiler){
instance.success = false
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:ready-transcript) => Instance is missing transcript compiler! (2)")
}
if (!instance.result){
instance.success = false
cancel()
throw new api.ODSystemError("ODAction(ot:create-transcript):ODWorker(ot:ready-transcript) => Instance is missing transcript result! (2)")
}
await opendiscord.events.get("onTranscriptReady").emit([opendiscord.transcripts,instance.result.ticket,instance.result.channel,instance.result.user])
if (instance.compiler.ready){
try{
@@ -109,9 +135,9 @@ export const registerActions = async () => {
//send channel message
if (transcriptConfig.data.general.enableChannel && channelMessage){
if (instance.pendingMessage && instance.pendingMessage.message && instance.pendingMessage.success){
if (instance.pendingMessage && instance.pendingMessage.success){
//edit "pending" message to be the "ready" message
instance.pendingMessage.message.edit(channelMessage.message)
instance.pendingMessage.message.edit(utilities.getMessageFromBuildResult(channelMessage,"message"))
}else{
//send ready message to channel
const post = opendiscord.posts.get("opendiscord:transcripts")
@@ -149,11 +175,11 @@ export const registerActions = async () => {
})
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:transcripts-created",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:transcripts-created",params.user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:transcripts-created",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:transcripts-created",params.user.id,1,"increase")
await opendiscord.events.get("afterTranscriptCreated").emit([opendiscord.transcripts,instance.result.ticket,instance.result.channel,instance.result.user])
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {user,channel,ticket} = params
opendiscord.log(user.displayName+" created a transcript!","info",[
{key:"user",value:user.username},
@@ -161,8 +187,8 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"option",value:ticket.option.id.value},
{key:"method",value:source,hidden:true},
{key:"compiler",value:instance.compiler.id.value},
{key:"method",value:origin,hidden:true},
{key:"compiler",value:instance.compiler?.id.value ?? "<unknown-compiler>"},
])
})
])
+17 -383
View File
@@ -1,16 +1,16 @@
///////////////////////////////////////
//TICKET DELETION SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:delete-ticket"))
opendiscord.actions.get("opendiscord:delete-ticket").workers.add([
new api.ODWorker("opendiscord:delete-ticket",3,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:delete-ticket",3,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to delete ticket! Open Ticket doesn't support threads!")
@@ -20,31 +20,18 @@ export const registerActions = async () => {
ticket.get("opendiscord:for-deletion").value = true
ticket.get("opendiscord:busy").value = true
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket deletion!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build(origin,{guild,channel,user,ticket,reason})).message)
//create transcript
if (!params.withoutTranscript){
const transcriptRes = await opendiscord.actions.get("opendiscord:create-transcript").run(source,{guild,channel,user,ticket})
const transcriptRes = await opendiscord.actions.get("opendiscord:create-transcript").run(origin,{guild,channel,user,ticket})
//transcript failure
if (typeof transcriptRes.success == "boolean" && !transcriptRes.success && transcriptRes.compiler){
const {compiler} = transcriptRes
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:transcript-error").build(source,{guild,channel,user,ticket,compiler,reason:transcriptRes.errorReason ?? null})).message)
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:transcript-error").build(origin,{guild,channel,user,ticket,compiler,reason:transcriptRes.errorReason ?? null})).message)
.catch((reason) => opendiscord.log("Unable to send transcript failure to ticket channel!","error",[{key:"id",value:channel.id}]))
//undo deletion
@@ -59,8 +46,8 @@ export const registerActions = async () => {
}
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-deleted",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-deleted",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-deleted",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-deleted",user.id,1,"increase")
//delete ticket from manager
opendiscord.tickets.remove(ticket.id)
@@ -68,21 +55,21 @@ export const registerActions = async () => {
//delete permissions from manager
await (await import("../data/framework/permissionLoader.js")).removeTicketPermissions(ticket)
}),
new api.ODWorker("opendiscord:discord-logs",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",2,async (instance,params,origin,cancel) => {
//logs before channel deletion => channel might still be used in log embeds
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(source,{guild,channel,user,ticket,mode:"delete",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:delete-channel",1,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
//delete channel & events
await opendiscord.events.get("onTicketChannelDeletion").emit([ticket,channel,user])
@@ -94,7 +81,7 @@ export const registerActions = async () => {
await opendiscord.events.get("afterTicketDeleted").emit([ticket,user,reason])
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" deleted a ticket!","info",[
@@ -103,7 +90,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source},
{key:"method",value:origin},
{key:"transcript",value:(!params.withoutTranscript).toString()},
])
})
@@ -113,357 +100,4 @@ export const registerActions = async () => {
params.ticket.get("opendiscord:busy").value = false
params.ticket.get("opendiscord:for-deletion").value = false
})
}
export const registerVerifyBars = async () => {
//DELETE TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:delete-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:delete-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source,cancel) => {
const {user,member,channel,guild} = instance
//check permissions
const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.delete,"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("button",{guild,channel,user,permissions:["support"]}))
return cancel()
}
//don't allow deleteWithoutTranscript to non-global-admins when enabled
if (params.data == "no-transcript" && generalConfig.data.system.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()
}
}
//check is in guild/server
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
//check if ticket exists
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//return when not allowed because of missing messages
if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
}
//start deleting ticket
if (params.data == "reason"){
//delete with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:delete-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//delete without reason
await instance.defer("update",false)
//don't await DELETE action => else it will update the message after the channel has been deleted
opendiscord.actions.get("opendiscord:delete-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true,withoutTranscript:(params.data == "no-transcript")})
//update ticket (for ticket message) => no-await doesn't wait for the action to set this variable
ticket.get("opendiscord:for-deletion").value = true
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:delete-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//DELETE TICKET CLOSE MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:delete-ticket-close-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-close-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:delete-ticket-close-message").success.add([
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source,cancel) => {
const {user,member,channel,guild} = instance
//check permissions
const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.delete,"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("button",{guild,channel,user,permissions:["support"]}))
return cancel()
}
//don't allow deleteWithoutTranscript to non-global-admins when enabled
if (params.data == "no-transcript" && generalConfig.data.system.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()
}
}
//check is in guild/server
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
//check if ticket exists
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//return when not allowed because of missing messages
if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
}
//start deleting ticket
if (params.data == "reason"){
//delete with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:delete-ticket-reason").build("close-message",{guild,channel,user,ticket}))
}else{
//delete without reason
await instance.defer("update",false)
//don't await DELETE action => else it will update the message after the channel has been deleted
opendiscord.actions.get("opendiscord:delete-ticket").run("close-message",{guild,channel,user,ticket,reason:null,sendMessage:false,withoutTranscript:(params.data == "no-transcript")})
//update ticket (for ticket message) => no-await doesn't wait for the action to set this variable
ticket.get("opendiscord:for-deletion").value = true
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build("close-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:delete-ticket-close-message").failure.add([
new api.ODWorker("opendiscord:back-to-close-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:close-message").build("other",{guild,channel,user,ticket,reason}))
})
])
//DELETE TICKET REOPEN MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:delete-ticket-reopen-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-reopen-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:delete-ticket-reopen-message").success.add([
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source,cancel) => {
const {user,member,channel,guild} = instance
//check permissions
const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.delete,"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("button",{guild,channel,user,permissions:["support"]}))
return cancel()
}
//don't allow deleteWithoutTranscript to non-global-admins when enabled
if (params.data == "no-transcript" && generalConfig.data.system.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()
}
}
//check is in guild/server
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
//check if ticket exists
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//return when not allowed because of missing messages
if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
}
//start deleting ticket
if (params.data == "reason"){
//delete with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:delete-ticket-reason").build("reopen-message",{guild,channel,user,ticket}))
}else{
//delete without reason
await instance.defer("update",false)
//don't await DELETE action => else it will update the message after the channel has been deleted
opendiscord.actions.get("opendiscord:delete-ticket").run("reopen-message",{guild,channel,user,ticket,reason:null,sendMessage:false,withoutTranscript:(params.data == "no-transcript")})
//update ticket (for ticket message) => no-await doesn't wait for the action to set this variable
ticket.get("opendiscord:for-deletion").value = true
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build("reopen-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:delete-ticket-reopen-message").failure.add([
new api.ODWorker("opendiscord:back-to-reopen-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build("other",{guild,channel,user,ticket,reason}))
})
])
//DELETE TICKET AUTOCLOSE MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:delete-ticket-autoclose-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-autoclose-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:delete-ticket-autoclose-message").success.add([
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source,cancel) => {
const {user,member,channel,guild} = instance
//check permissions
const permsResult = await opendiscord.permissions.checkCommandPerms(generalConfig.data.system.permissions.delete,"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("button",{guild,channel,user,permissions:["support"]}))
return cancel()
}
//don't allow deleteWithoutTranscript to non-global-admins when enabled
if (params.data == "no-transcript" && generalConfig.data.system.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()
}
}
//check is in guild/server
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
//check if ticket exists
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//return when not allowed because of missing messages
if (!permsResult.isAdmin && (!generalConfig.data.system.allowCloseBeforeMessage || !generalConfig.data.system.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
if (analysis && !generalConfig.data.system.allowCloseBeforeMessage && analysis.totalMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
if (analysis && !generalConfig.data.system.allowCloseBeforeAdminMessage && analysis.adminMessages < 1){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:lang.getTranslation("errors.descriptions.closeBeforeAdminMessage"),customTitle:lang.getTranslation("errors.titles.noPermissions")}))
return cancel()
}
}
//start deleting ticket
if (params.data == "reason"){
//delete with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:delete-ticket-reason").build("autoclose-message",{guild,channel,user,ticket}))
}else{
//delete without reason
await instance.defer("update",false)
//don't await DELETE action => else it will update the message after the channel has been deleted
opendiscord.actions.get("opendiscord:delete-ticket").run("autoclose-message",{guild,channel,user,ticket,reason:null,sendMessage:false,withoutTranscript:(params.data == "no-transcript")})
//update ticket (for ticket message) => no-await doesn't wait for the action to set this variable
ticket.get("opendiscord:for-deletion").value = true
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build("autoclose-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:delete-ticket-autoclose-message").failure.add([
new api.ODWorker("opendiscord:back-to-autoclose-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:autoclose-message").build("other",{guild,channel,user,ticket}))
})
])
}
+47 -113
View File
@@ -1,76 +1,43 @@
///////////////////////////////////////
//TRANSCRIPT ERROR SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerButtonResponders = async () => {
export async function registerButtonResponders(){
//TRANSCRIPT ERROR RETRY
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:transcript-error-retry",/^od:transcript-error-retry_([^_]+)/))
opendiscord.responders.buttons.get("opendiscord:transcript-error-retry").workers.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.delete
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const originalSource = instance.interaction.customId.split("_")[1] as api.ODActionManagerIds_Default["opendiscord:delete-ticket"]["source"]
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,origin,cancel) => {
const {guild,channel,user,member} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//responder checks
const hasPerms = await openticketUtils.replyHasPermissions(instance,origin,"delete")
if (!hasPerms) return cancel()
const isInGuild = await openticketUtils.replyIsInGuild(instance,origin)
if (!isInGuild || !guild || channel.isDMBased()) return cancel()
const ticket = await openticketUtils.replyIsTicket(instance,origin)
if (!ticket) return cancel()
const isAvailable = await openticketUtils.replyTicketIsAvailable(instance,origin,ticket)
if (!isAvailable) return cancel()
//fetch data
const originalOrigin = instance.interaction.customId.split("_")[1] as api.ODActionManagerIdMappings["opendiscord:delete-ticket"]["origin"]
//start deleting ticket (without reason)
await instance.defer("update",false)
//don't await DELETE action => else it will update the message after the channel has been deleted
opendiscord.actions.get("opendiscord:delete-ticket").run(originalSource,{guild,channel,user,ticket,reason:"Transcript Error (Retried)",sendMessage:false,withoutTranscript:false})
//update ticket (for ticket message) => no-await doesn't wait for the action to set this variable
ticket.get("opendiscord:for-deletion").value = true
opendiscord.actions.get("opendiscord:delete-ticket").run(originalOrigin,{guild,channel,user,ticket,reason:"Transcript Error (Retried)",sendMessage:false,withoutTranscript:false})
ticket.get("opendiscord:for-deletion").value = true //disable ticket message buttons
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build("other",{guild,channel,user,ticket,reason:"Transcript Error (Retried)"}))
}),
new api.ODWorker("opendiscord:logs",-1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",-1,async (instance,params,origin,cancel) => {
const {user,channel} = instance
if (channel.isDMBased()) return
opendiscord.log(user.displayName+" retried deleting a ticket with transcript!","info",[
@@ -78,7 +45,7 @@ export const registerButtonResponders = async () => {
{key:"userid",value:user.id,hidden:true},
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
@@ -86,68 +53,35 @@ export const registerButtonResponders = async () => {
//TRANSCRIPT ERROR CONTINUE
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:transcript-error-continue",/^od:transcript-error-continue_([^_]+)/))
opendiscord.responders.buttons.get("opendiscord:transcript-error-continue").workers.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.delete
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,origin,cancel) => {
const {guild,channel,user} = instance
const originalSource = instance.interaction.customId.split("_")[1] as api.ODActionManagerIds_Default["opendiscord:delete-ticket"]["source"]
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//responder checks
const hasPerms = await openticketUtils.replyHasPermissions(instance,origin,"delete")
if (!hasPerms) return cancel()
const isInGuild = await openticketUtils.replyIsInGuild(instance,origin)
if (!isInGuild || !guild || channel.isDMBased()) return cancel()
const ticket = await openticketUtils.replyIsTicket(instance,origin)
if (!ticket) return cancel()
const isAvailable = await openticketUtils.replyTicketIsAvailable(instance,origin,ticket)
if (!isAvailable) return cancel()
//start deleting ticket (without reason)
//fetch data
const originalOrigin = instance.interaction.customId.split("_")[1] as api.ODActionManagerIdMappings["opendiscord:delete-ticket"]["origin"]
//start deleting ticket (without reason & without transcript)
await instance.defer("update",false)
//don't await DELETE action => else it will update the message after the channel has been deleted
opendiscord.actions.get("opendiscord:delete-ticket").run(originalSource,{guild,channel,user,ticket,reason:"Transcript Error (Continued)",sendMessage:false,withoutTranscript:true})
//update ticket (for ticket message) => no-await doesn't wait for the action to set this variable
ticket.get("opendiscord:for-deletion").value = true
opendiscord.actions.get("opendiscord:delete-ticket").run(originalOrigin,{guild,channel,user,ticket,reason:"Transcript Error (Continued)",sendMessage:false,withoutTranscript:true})
ticket.get("opendiscord:for-deletion").value = true //disable ticket message buttons
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build("other",{guild,channel,user,ticket,reason:"Transcript Error (Continued)"}))
}),
new api.ODWorker("opendiscord:logs",-1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",-1,async (instance,params,origin,cancel) => {
const {user,channel} = instance
if (channel.isDMBased()) return
opendiscord.log(user.displayName+" continued deleting a ticket without transcript!","info",[
@@ -155,7 +89,7 @@ export const registerButtonResponders = async () => {
{key:"userid",value:user.id,hidden:true},
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+13 -23
View File
@@ -1,32 +1,22 @@
///////////////////////////////////////
//VERIFYBAR SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
export const registerButtonResponders = async () => {
//VERIFYBAR SUCCESS
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:verifybar-success",/^od:verifybar-success_([^_]+)/))
opendiscord.responders.buttons.get("opendiscord:verifybar-success").workers.add(
new api.ODWorker("opendiscord:handle-verifybar",0,async (instance,params,source,cancel) => {
const id = instance.interaction.customId.split("_")[1]
const customData = instance.interaction.customId.split("_")[2] as string|undefined
export async function registerButtonResponders(){
//HANDLE VERIFYBAR BUTTON
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:verifybar-button",/^od:verifybar\|([^|]+)\|([^|]+)/))
opendiscord.responders.buttons.get("opendiscord:verifybar-button").workers.add(
new api.ODWorker("opendiscord:handle-verifybar",0,async (instance,params,origin,cancel) => {
const match = /^od:verifybar\|([^|]+)\|([^|]+)/.exec(instance.interaction.customId)
if (!match) return cancel()
const verifyBarId = match[1]
const verifyButtonId = match[2]
const verifybar = opendiscord.verifybars.get(id)
const verifybar = opendiscord.verifybars.get(verifyBarId)
if (!verifybar) return
if (verifybar.success) await verifybar.success.executeWorkers(instance,"verifybar",{data:customData ?? null,verifybarMessage:instance.message})
})
)
//VERIFYBAR FAILURE
opendiscord.responders.buttons.add(new api.ODButtonResponder("opendiscord:verifybar-failure",/^od:verifybar-failure_([^_]+)/))
opendiscord.responders.buttons.get("opendiscord:verifybar-failure").workers.add(
new api.ODWorker("opendiscord:handle-verifybar",0,async (instance,params,source,cancel) => {
const id = instance.interaction.customId.split("_")[1]
const customData = instance.interaction.customId.split("_")[2] as string|undefined
const verifybar = opendiscord.verifybars.get(id)
if (!verifybar) return
if (verifybar.failure) await verifybar.failure.executeWorkers(instance,"verifybar",{data:customData ?? null,verifybarMessage:instance.message})
await verifybar.activate(instance,verifyButtonId)
})
)
}
+47 -102
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//TICKET MOVING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:move-ticket"))
opendiscord.actions.get("opendiscord:move-ticket").workers.add([
new api.ODWorker("opendiscord:move-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:move-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason,data} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to move ticket! Open Ticket doesn't support threads!")
@@ -17,78 +17,31 @@ export const registerActions = async () => {
ticket.option = data
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-moved",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-moved",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-moved",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-moved",user.id,1,"increase")
//get new channel properties
const channelPrefix = ticket.option.get("opendiscord:channel-prefix").value
const channelSuffix = ticket.get("opendiscord:channel-suffix").value
const channelCategory = ticket.option.get("opendiscord:channel-category").value
const channelBackupCategory = ticket.option.get("opendiscord:channel-category-backup").value
const rawClaimCategory = ticket.option.get("opendiscord:channel-categories-claimed").value.find((c) => c.user == user.id)
const claimCategory = (rawClaimCategory) ? rawClaimCategory.category : null
const closeCategory = ticket.option.get("opendiscord:channel-category-closed").value
const channelTopic = ticket.option.get("opendiscord:channel-topic").value
//handle category
let category: string|null = null
let categoryMode: "backup"|"normal"|"closed"|"claimed"|null = null
if (claimCategory){
//use claim category
category = claimCategory
categoryMode = "claimed"
}else if (closeCategory != "" && ticket.get("opendiscord:closed").value){
//use close category
category = closeCategory
categoryMode = "closed"
}else if (channelCategory != ""){
//category enabled
const normalCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelCategory)
if (!normalCategory){
//default category was not found
opendiscord.log("Ticket Move Error: Unable to find category! #1","error",[
{key:"categoryid",value:channelCategory},
{key:"backup",value:"false"}
//calculate & update category
const categoryResult = await opendiscord.actions.get("opendiscord:calculate-ticket-category").run("move-ticket",{guild,user,option:ticket.option,channel,ticket,currentCategoryId:channel.parentId})
if (categoryResult && categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryId !== "undefined" && typeof categoryResult.newCategoryMode !== "undefined" && typeof categoryResult.newCategory !== "undefined"){
const originalCategoryName = channel.parent?.name ?? "<unknown>"
const newCategoryName = categoryResult.newCategory?.name ?? "<unknown>"
try{
await utilities.timedAwait(channel.setParent(categoryResult.newCategoryId,{lockPermissions:false}),3000,(err) => {
process.emit("uncaughtException",new Error("Error: Unable to change channel parent: "+err))
})
ticket.get("opendiscord:category-mode").value = categoryResult.newCategoryMode
ticket.get("opendiscord:category").value = categoryResult.newCategoryId
}catch(err){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-category").build("ticket-move",{guild,channel,user,originalCategory:originalCategoryName,newCategory:newCategoryName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
opendiscord.log("Unable to move ticket to moved category.","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"categoryid",value:categoryResult.newCategoryId ?? "/"}
])
}else{
//default category was found
if (normalCategory.children.cache.size >= 50 && channelBackupCategory != ""){
//use backup category
const backupCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelBackupCategory)
if (!backupCategory){
//default category was not found
opendiscord.log("Ticket Move Error: Unable to find category! #2","error",[
{key:"categoryid",value:channelBackupCategory},
{key:"backup",value:"true"}
])
}else{
category = backupCategory.id
categoryMode = "backup"
}
}else{
//use default category
category = normalCategory.id
categoryMode = "normal"
}
}
}
try {
//only move category when not the same.
if (channel.parentId != category) await utilities.timedAwait(channel.setParent(category,{lockPermissions:false}),2500,(err) => {
opendiscord.log("Failed to change channel category on ticket move","error")
})
ticket.get("opendiscord:category-mode").value = categoryMode
ticket.get("opendiscord:category").value = category
}catch(e){
opendiscord.log("Unable to move ticket to 'moved category'!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"categoryid",value:category ?? "/"}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
//handle permissions
const permissions: discord.OverwriteResolvable[] = [{
type:discord.OverwriteType.Role,
@@ -153,58 +106,50 @@ export const registerActions = async () => {
ticket.get("opendiscord:participants").value = participants
ticket.get("opendiscord:participants").refreshDatabase()
//rename channel (and give error when crashed)
const pinEmoji = ticket.get("opendiscord:pinned").value ? generalConfig.data.system.pinEmoji : ""
const priorityEmoji = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).channelEmoji ?? ""
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(channelPrefix+channelSuffix)
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket move","error")
})
}catch(err){
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-move",{guild,channel,user,originalName,newName:newName})).message)
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("move-ticket",{guild,user,option:ticket.option,channel,ticket,currentChannelName:channel.name})
if (channelNameResult && channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined"){
const originalName = channel.name
const newName = channelNameResult.newChannelName
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket moving!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket move","error")
})
}catch(err){
opendiscord.log("Unable to rename channel while moving ticket! Waiting until ratelimit expires...","warning",[
{key:"oldName",value:originalName},
{key:"newName",value:newName}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-move",{guild,channel,user,originalName,newName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:move-message").build(source,{guild,channel,user,ticket,reason,data})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:move-message").build(origin,{guild,channel,user,ticket,reason,data})).message)
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketMoved").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"move",reason,additionalData:data}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" moved a ticket!","info",[
@@ -213,7 +158,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+38 -221
View File
@@ -1,15 +1,16 @@
///////////////////////////////////////
//TICKET PINNING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:pin-ticket"))
opendiscord.actions.get("opendiscord:pin-ticket").workers.add([
new api.ODWorker("opendiscord:pin-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:pin-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to pin ticket! Open Ticket doesn't support threads!")
@@ -22,66 +23,66 @@ export const registerActions = async () => {
ticket.get("opendiscord:busy").value = true
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-pinned",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-pinned",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-pinned",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-pinned",user.id,1,"increase")
//move to top of category
if (channel.parent){
await channel.setPosition(0,{reason:"Ticket Pinned!"})
}
//rename channel (and give error when crashed)
const pinEmoji = ticket.get("opendiscord:pinned").value ? generalConfig.data.system.pinEmoji : ""
const priorityEmoji = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).channelEmoji ?? ""
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(channel.name)
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket pin","error")
})
}catch(err){
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-pin",{guild,channel,user,originalName,newName})).message)
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("pin-ticket",{guild,user,option:ticket.option,channel,ticket,currentChannelName:channel.name})
if (channelNameResult && channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined"){
const originalName = channel.name
const newName = channelNameResult.newChannelName
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket pinning!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"message",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket pin","error")
})
}catch(err){
opendiscord.log("Unable to rename channel while pinning ticket! Waiting until ratelimit expires...","warning",[
{key:"oldName",value:originalName},
{key:"newName",value:newName}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-pin",{guild,channel,user,originalName,newName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:pin-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:pin-message").build(origin,{guild,channel,user,ticket,reason})).message)
if (sentMsg) await interactiveMsgState.setMsgState({channel,message:sentMsg},{
messageType:"pin-message",
messageOrigin:"other",
messageAuthor:user.id,
messageReason:reason
},false)
}
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketPinned").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"pin",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" pinned a ticket!","info",[
@@ -90,192 +91,8 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
}
export const registerVerifyBars = async () => {
//PIN TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:pin-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:pin-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.pin
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:pin-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when already pinned
if (ticket.get("opendiscord:pinned").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.pin"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start pinning ticket
if (params.data == "reason"){
//pin with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:pin-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//pin without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:pin-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:pin-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//PIN TICKET UNPIN MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:pin-ticket-unpin-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-unpin-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:pin-ticket-unpin-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.pin
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:pin-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when already pinned
if (ticket.get("opendiscord:pinned").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.pin"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start pinning ticket
if (params.data == "reason"){
//pin with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:pin-ticket-reason").build("unpin-message",{guild,channel,user,ticket}))
}else{
//pin without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:pin-ticket").run("unpin-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:pin-message").build("unpin-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:pin-ticket-unpin-message").failure.add([
new api.ODWorker("opendiscord:back-to-unpin-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:unpin-message").build("other",{guild,channel,user,ticket,reason}))
})
])
opendiscord.actions.get("opendiscord:pin-ticket").workers.backupWorker = new api.ODWorker("opendiscord:cancel-busy",0,(instance,params) => {
//set busy to false in case of crash or cancel
params.ticket.get("opendiscord:busy").value = false
})
}
+9 -9
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//REACTION ROLE SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:reaction-role"))
opendiscord.actions.get("opendiscord:reaction-role").workers.add([
new api.ODWorker("opendiscord:reaction-role",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:reaction-role",2,async (instance,params,origin,cancel) => {
const {guild,user,option,overwriteMode} = params
const role = opendiscord.roles.get(option.id)
if (!role) throw new api.ODSystemError("ODAction(ot:reaction-role) => Unknown reaction role (ODRole)")
@@ -83,25 +83,25 @@ export const registerActions = async () => {
instance.result = result
await opendiscord.events.get("afterRolesUpdated").emit([user,role])
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
const {guild,user,option,overwriteMode} = params
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(source,{guild,user,role:instance.role,result:instance.result}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,user,option} = params
opendiscord.log(user.displayName+" updated his roles!","info",[
{key:"user",value:user.username},
{key:"userid",value:user.id,hidden:true},
{key:"method",value:source},
{key:"method",value:origin},
{key:"option",value:option.id.value}
])
})
+12 -24
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//TICKET REMOVE USER SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:remove-ticket-user"))
opendiscord.actions.get("opendiscord:remove-ticket-user").workers.add([
new api.ODWorker("opendiscord:remove-ticket-user",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:remove-ticket-user",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason,data} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to remove user from ticket! Open Ticket doesn't support threads!")
@@ -29,43 +29,31 @@ export const registerActions = async () => {
opendiscord.log("Failed to remove channel permission overwrites on remove-ticket-user","error")
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket user removal!","error",[
{key:"channel",value:channel.id},
{key:"message",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:remove-message").build(source,{guild,channel,user,ticket,reason,data})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:remove-message").build(origin,{guild,channel,user,ticket,reason,data})).message)
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketUserRemoved").emit([ticket,user,data,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"remove",reason,additionalData:data}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,data} = params
opendiscord.log(user.displayName+" removed "+data.displayName+" from a ticket!","info",[
@@ -74,7 +62,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+29 -34
View File
@@ -1,72 +1,67 @@
///////////////////////////////////////
//TICKET RENAMING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:rename-ticket"))
opendiscord.actions.get("opendiscord:rename-ticket").workers.add([
new api.ODWorker("opendiscord:rename-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:rename-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason,data} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to rename ticket! Open Ticket doesn't support threads!")
await opendiscord.events.get("onTicketRename").emit([ticket,user,channel,reason])
//rename channel (and give error when crashed)
const pinEmoji = ticket.get("opendiscord:pinned").value ? generalConfig.data.system.pinEmoji : ""
const priorityEmoji = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).channelEmoji ?? ""
//update ticket
ticket.get("opendiscord:channel-renamed").value = data
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(data)
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket rename","error")
})
}catch(err){
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-rename",{guild,channel,user,originalName,newName:data})).message)
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("rename-ticket",{guild,user,option:ticket.option,channel,ticket,currentChannelName:channel.name})
if (channelNameResult && channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined"){
const originalName = channel.name
const newName = channelNameResult.newChannelName
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket renaming!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket rename","error")
})
}catch(err){
opendiscord.log("Unable to rename channel while renaming ticket! Waiting until ratelimit expires...","warning",[
{key:"oldName",value:originalName},
{key:"newName",value:newName}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-rename",{guild,channel,user,originalName,newName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:rename-message").build(source,{guild,channel,user,ticket,reason,data})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:rename-message").build(origin,{guild,channel,user,ticket,reason,data})).message)
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketRenamed").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"rename",reason,additionalData:data}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" renamed a ticket!","info",[
@@ -75,7 +70,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+40 -344
View File
@@ -1,15 +1,16 @@
///////////////////////////////////////
//TICKET REOPENING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:reopen-ticket"))
opendiscord.actions.get("opendiscord:reopen-ticket").workers.add([
new api.ODWorker("opendiscord:reopen-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:reopen-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to reopen ticket! Open Ticket doesn't support threads!")
@@ -28,65 +29,37 @@ export const registerActions = async () => {
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
}
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-reopened",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-reopened",user.id,1,"increase")
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-reopened",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-reopened",user.id,1,"increase")
//update category
//calculate & update category
if (typeof params.allowCategoryChange == "boolean" ? params.allowCategoryChange : true){
const channelCategory = ticket.option.get("opendiscord:channel-category").value
const channelBackupCategory = ticket.option.get("opendiscord:channel-category-backup").value
if (channelCategory !== ""){
//category enabled
try {
const normalCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelCategory)
if (!normalCategory){
//default category was not found
opendiscord.log("Ticket Reopening Error: Unable to find category! #1","error",[
{key:"categoryid",value:channelCategory},
{key:"backup",value:"false"}
])
}else{
//default category was found
if (normalCategory.children.cache.size >= 49 && channelBackupCategory != ""){
//use backup category
const backupCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelBackupCategory)
if (!backupCategory){
//default category was not found
opendiscord.log("Ticket Reopening Error: Unable to find category! #2","error",[
{key:"categoryid",value:channelBackupCategory},
{key:"backup",value:"true"}
])
}else{
//use backup category
channel.setParent(backupCategory,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = "backup"
ticket.get("opendiscord:category").value = backupCategory.id
}
}else{
//use default category
channel.setParent(normalCategory,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = "normal"
ticket.get("opendiscord:category").value = normalCategory.id
}
}
}catch(e){
opendiscord.log("Unable to move ticket to 'reopened category'!","error",[
const categoryResult = await opendiscord.actions.get("opendiscord:calculate-ticket-category").run("reopen-ticket",{guild,user,option:ticket.option,channel,ticket,currentCategoryId:channel.parentId})
if (categoryResult && categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryId !== "undefined" && typeof categoryResult.newCategoryMode !== "undefined" && typeof categoryResult.newCategory !== "undefined"){
const originalCategoryName = channel.parent?.name ?? "<unknown>"
const newCategoryName = categoryResult.newCategory?.name ?? "<unknown>"
try{
await utilities.timedAwait(channel.setParent(categoryResult.newCategoryId,{lockPermissions:false}),3000,(err) => {
process.emit("uncaughtException",new Error("Error: Unable to change channel parent: "+err))
})
ticket.get("opendiscord:category-mode").value = categoryResult.newCategoryMode
ticket.get("opendiscord:category").value = categoryResult.newCategoryId
}catch(err){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-category").build("ticket-reopen",{guild,channel,user,originalCategory:originalCategoryName,newCategory:newCategoryName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
opendiscord.log("Unable to move ticket to reopened category.","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"categoryid",value:categoryResult.newCategoryId ?? "/"}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}else{
channel.setParent(null,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = null
ticket.get("opendiscord:category").value = null
}
}
@@ -141,43 +114,38 @@ export const registerActions = async () => {
})
channel.permissionOverwrites.set(permissions)
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket reopening!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build(origin,{guild,channel,user,ticket,reason})).message)
if (sentMsg) await interactiveMsgState.setMsgState({channel,message:sentMsg},{
messageType:"reopen-message",
messageOrigin:"other",
messageAuthor:user.id,
messageReason:reason
},false)
}
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketReopened").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"reopen",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" reopened a ticket!","info",[
@@ -186,280 +154,8 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
}
export const registerVerifyBars = async () => {
//REOPEN TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:reopen-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:reopen-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.reopen
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:reopen-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not closed
if (!ticket.get("opendiscord:closed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.reopen"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start reopening ticket
if (params.data == "reason"){
//reopen with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:reopen-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//reopen without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:reopen-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:reopen-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//REOPEN TICKET CLOSE MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:reopen-ticket-close-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-close-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:reopen-ticket-close-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.reopen
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:reopen-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not closed
if (!ticket.get("opendiscord:closed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.reopen"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start reopening ticket
if (params.data == "reason"){
//reopen with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:reopen-ticket-reason").build("close-message",{guild,channel,user,ticket}))
}else{
//reopen without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:reopen-ticket").run("close-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build("close-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:reopen-ticket-close-message").failure.add([
new api.ODWorker("opendiscord:back-to-close-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:close-message").build("other",{guild,channel,user,ticket,reason}))
})
])
//REOPEN TICKET AUTOCLOSE MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:reopen-ticket-autoclose-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-autoclose-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:reopen-ticket-autoclose-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.reopen
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:reopen-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not closed
if (!ticket.get("opendiscord:closed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.reopen"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start reopening ticket
if (params.data == "reason"){
//reopen with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:reopen-ticket-reason").build("autoclose-message",{guild,channel,user,ticket}))
}else{
//reopen without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:reopen-ticket").run("autoclose-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build("autoclose-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:reopen-ticket-autoclose-message").failure.add([
new api.ODWorker("opendiscord:back-to-autoclose-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:autoclose-message").build("other",{guild,channel,user,ticket}))
})
])
opendiscord.actions.get("opendiscord:reopen-ticket").workers.backupWorker = new api.ODWorker("opendiscord:cancel-busy",0,(instance,params) => {
//set busy to false in case of crash or cancel
params.ticket.get("opendiscord:busy").value = false
})
}
+26 -38
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//TICKET TRANSFER SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:transfer-ticket"))
opendiscord.actions.get("opendiscord:transfer-ticket").workers.add([
new api.ODWorker("opendiscord:transfer-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:transfer-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason,newCreator} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to transfer ticket! Open Ticket doesn't support threads!")
@@ -33,12 +33,8 @@ export const registerActions = async () => {
}
//update stats
await opendiscord.stats.get("opendiscord:global").setStat("opendiscord:tickets-transferred",1,"increase")
await opendiscord.stats.get("opendiscord:user").setStat("opendiscord:tickets-transferred",user.id,1,"increase")
//get new channel properties
const channelPrefix = ticket.option.get("opendiscord:channel-prefix").value
const channelSuffix = ticket.get("opendiscord:channel-suffix").value
await opendiscord.statistics.get("opendiscord:global").setStat("opendiscord:tickets-transferred",1,"increase")
await opendiscord.statistics.get("opendiscord:user").setStat("opendiscord:tickets-transferred",user.id,1,"increase")
//handle permissions
const permissions: discord.OverwriteResolvable[] = [{
@@ -93,48 +89,40 @@ export const registerActions = async () => {
opendiscord.log("Failed to reset channel permissions on ticket transfer!","error")
}
//rename channel (and give error when crashed)
const pinEmoji = ticket.get("opendiscord:pinned").value ? generalConfig.data.system.pinEmoji : ""
const priorityEmoji = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).channelEmoji ?? ""
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(channelPrefix+channelSuffix)
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket transfer","error")
})
}catch(err){
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-transfer",{guild,channel,user,originalName,newName:newName})).message)
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("transfer-ticket",{guild,user,option:ticket.option,channel,ticket,currentChannelName:channel.name})
if (channelNameResult && channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined"){
const originalName = channel.name
const newName = channelNameResult.newChannelName
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket transferring!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket transfer","error")
})
}catch(err){
opendiscord.log("Unable to rename channel while transferring ticket! Waiting until ratelimit expires...","warning",[
{key:"oldName",value:originalName},
{key:"newName",value:newName}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-transfer",{guild,channel,user,originalName,newName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:transfer-message").build(source,{guild,channel,user,ticket,oldCreator,newCreator,reason})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:transfer-message").build(origin,{guild,channel,user,ticket,oldCreator,newCreator,reason})).message)
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketTransferred").emit([ticket,user,channel,oldCreator,newCreator,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,newCreator,reason} = params
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,newCreator} = params
opendiscord.log(user.displayName+" transferred a ticket to '"+newCreator.displayName+"'!","info",[
@@ -143,7 +131,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+38 -255
View File
@@ -1,15 +1,16 @@
///////////////////////////////////////
//TICKET UNCLAIMING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:unclaim-ticket"))
opendiscord.actions.get("opendiscord:unclaim-ticket").workers.add([
new api.ODWorker("opendiscord:unclaim-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:unclaim-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to unclaim ticket! Open Ticket doesn't support threads!")
@@ -21,97 +22,63 @@ export const registerActions = async () => {
ticket.get("opendiscord:claimed-on").value = null
ticket.get("opendiscord:busy").value = true
//update category
//calculate & update category
if (typeof params.allowCategoryChange == "boolean" ? params.allowCategoryChange : true){
const channelCategory = ticket.option.get("opendiscord:channel-category").value
const channelBackupCategory = ticket.option.get("opendiscord:channel-category-backup").value
if (channelCategory !== ""){
//category enabled
try {
const normalCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelCategory)
if (!normalCategory){
//default category was not found
opendiscord.log("Ticket Unclaiming Error: Unable to find category! #1","error",[
{key:"categoryid",value:channelCategory},
{key:"backup",value:"false"}
])
}else{
//default category was found
if (normalCategory.children.cache.size >= 49 && channelBackupCategory != ""){
//use backup category
const backupCategory = await opendiscord.client.fetchGuildCategoryChannel(guild,channelBackupCategory)
if (!backupCategory){
//default category was not found
opendiscord.log("Ticket Unclaiming Error: Unable to find category! #2","error",[
{key:"categoryid",value:channelBackupCategory},
{key:"backup",value:"true"}
])
}else{
//use backup category
channel.setParent(backupCategory,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = "backup"
ticket.get("opendiscord:category").value = backupCategory.id
}
}else{
//use default category
channel.setParent(normalCategory,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = "normal"
ticket.get("opendiscord:category").value = normalCategory.id
}
}
}catch(e){
opendiscord.log("Unable to move ticket to 'unclaimed category'!","error",[
const categoryResult = await opendiscord.actions.get("opendiscord:calculate-ticket-category").run("unclaim-ticket",{guild,user,option:ticket.option,channel,ticket,currentCategoryId:channel.parentId})
if (categoryResult && categoryResult.shouldChangeCategory && typeof categoryResult.newCategoryId !== "undefined" && typeof categoryResult.newCategoryMode !== "undefined" && typeof categoryResult.newCategory !== "undefined"){
const originalCategoryName = channel.parent?.name ?? "<unknown>"
const newCategoryName = categoryResult.newCategory?.name ?? "<unknown>"
try{
await utilities.timedAwait(channel.setParent(categoryResult.newCategoryId,{lockPermissions:false}),3000,(err) => {
process.emit("uncaughtException",new Error("Error: Unable to change channel parent: "+err))
})
ticket.get("opendiscord:category-mode").value = categoryResult.newCategoryMode
ticket.get("opendiscord:category").value = categoryResult.newCategoryId
}catch(err){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-category").build("ticket-unclaim",{guild,channel,user,originalCategory:originalCategoryName,newCategory:newCategoryName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
opendiscord.log("Unable to move ticket to unclaimed category.","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true}
{key:"channelid",value:channel.id,hidden:true},
{key:"categoryid",value:categoryResult.newCategoryId ?? "/"}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}else{
channel.setParent(null,{lockPermissions:false})
ticket.get("opendiscord:category-mode").value = null
ticket.get("opendiscord:category").value = null
}
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket unclaiming!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:unclaim-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:unclaim-message").build(origin,{guild,channel,user,ticket,reason})).message)
if (sentMsg) await interactiveMsgState.setMsgState({channel,message:sentMsg},{
messageType:"unclaim-message",
messageOrigin:"other",
messageAuthor:user.id,
messageReason:reason
},false)
}
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketUnclaimed").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"unclaim",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" unclaimed a ticket!","info",[
@@ -120,192 +87,8 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
}
export const registerVerifyBars = async () => {
//UNCLAIM TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:unclaim-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:unclaim-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.unclaim
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:unclaim-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not claimed
if (!ticket.get("opendiscord:claimed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.unclaim"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start unclaiming ticket
if (params.data == "reason"){
//unclaim with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:unclaim-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//unclaim without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:unclaim-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:unclaim-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//UNCLAIM TICKET CLAIM MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:unclaim-ticket-claim-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-claim-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:unclaim-ticket-claim-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.unclaim
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:unclaim-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not claimed
if (!ticket.get("opendiscord:claimed").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.unclaim"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start unclaiming ticket
if (params.data == "reason"){
//unclaim with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:unclaim-ticket-reason").build("claim-message",{guild,channel,user,ticket}))
}else{
//unclaim without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:unclaim-ticket").run("claim-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:unclaim-message").build("claim-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:unclaim-ticket-claim-message").failure.add([
new api.ODWorker("opendiscord:back-to-claim-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:claim-message").build("other",{guild,channel,user,ticket,reason}))
})
])
opendiscord.actions.get("opendiscord:unclaim-ticket").workers.backupWorker = new api.ODWorker("opendiscord:cancel-busy",0,(instance,params) => {
//set busy to false in case of crash or cancel
params.ticket.get("opendiscord:busy").value = false
})
}
+36 -219
View File
@@ -1,15 +1,16 @@
///////////////////////////////////////
//TICKET UNPINNING SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:unpin-ticket"))
opendiscord.actions.get("opendiscord:unpin-ticket").workers.add([
new api.ODWorker("opendiscord:unpin-ticket",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:unpin-ticket",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,reason} = params
if (channel.isThread()) throw new api.ODSystemError("Unable to unpin ticket! Open Ticket doesn't support threads!")
@@ -21,58 +22,58 @@ export const registerActions = async () => {
ticket.get("opendiscord:pinned-on").value = null
ticket.get("opendiscord:busy").value = true
//rename channel (and give error when crashed)
const pinEmoji = ticket.get("opendiscord:pinned").value ? generalConfig.data.system.pinEmoji : ""
const priorityEmoji = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value).channelEmoji ?? ""
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(channel.name)
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket unpin","error")
})
}catch(err){
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-unpin",{guild,channel,user,originalName,newName})).message)
}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (ticketMessage){
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("unpin-ticket",{guild,user,option:ticket.option,channel,ticket,currentChannelName:channel.name})
if (channelNameResult && channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined"){
const originalName = channel.name
const newName = channelNameResult.newChannelName
try{
ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message on ticket unpinning!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket unpin","error")
})
}catch(err){
opendiscord.log("Unable to rename channel while unpinning ticket! Waiting until ratelimit expires...","warning",[
{key:"oldName",value:originalName},
{key:"newName",value:newName}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-unpin",{guild,channel,user,originalName,newName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
}
}
//update ticket message (no await)
openticketUtils.updateTicketMessage(guild,channel,user,ticket)
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:unpin-message").build(source,{guild,channel,user,ticket,reason})).message)
if (params.sendMessage){
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:unpin-message").build(origin,{guild,channel,user,ticket,reason})).message)
if (sentMsg) await interactiveMsgState.setMsgState({channel,message:sentMsg},{
messageType:"unpin-message",
messageOrigin:"other",
messageAuthor:user.id,
messageReason:reason
},false)
}
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketUnpinned").emit([ticket,user,channel,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
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(source,{guild,channel,user,ticket,mode:"unpin",reason,additionalData:null}))
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(source,{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,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
opendiscord.log(user.displayName+" unpinned a ticket!","info",[
@@ -81,192 +82,8 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
}
export const registerVerifyBars = async () => {
//UNPIN TICKET TICKET MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:unpin-ticket-ticket-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-ticket-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:unpin-ticket-ticket-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.unpin
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:unpin-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not pinned
if (!ticket.get("opendiscord:pinned").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.unpin"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start unpining ticket
if (params.data == "reason"){
//unpin with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:unpin-ticket-reason").build("ticket-message",{guild,channel,user,ticket}))
}else{
//unpin without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:unpin-ticket").run("ticket-message",{guild,channel,user,ticket,reason:null,sendMessage:true})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
}
})
])
opendiscord.verifybars.get("opendiscord:unpin-ticket-ticket-message").failure.add([
new api.ODWorker("opendiscord:back-to-ticket-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket}))
})
])
//UNPIN TICKET PIN MESSAGE
opendiscord.verifybars.add(new api.ODVerifyBar("opendiscord:unpin-ticket-pin-message",opendiscord.builders.messages.getSafe("opendiscord:verifybar-pin-message"),!generalConfig.data.system.disableVerifyBars))
opendiscord.verifybars.get("opendiscord:unpin-ticket-pin-message").success.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.unpin
if (permissionMode == "none"){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else if (permissionMode == "everyone") return
else if (permissionMode == "admin"){
if (!opendiscord.permissions.hasPermissions("support",await opendiscord.permissions.getPermissions(instance.user,instance.channel,instance.guild))){
//no permissions
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()
}else return
}else{
if (!instance.guild || !instance.member){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #1",layout:"advanced"}))
return cancel()
}
const role = await opendiscord.client.fetchGuildRole(instance.guild,permissionMode)
if (!role){
//error
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Permission Error: Not in Server #2",layout:"advanced"}))
return cancel()
}
if (!role.members.has(instance.member.id)){
//no permissions
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build("button",{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:unpin-ticket",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
//return when not pinned
if (!ticket.get("opendiscord:pinned").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.unpin"),layout:"simple"}))
return cancel()
}
//return when busy
if (ticket.get("opendiscord:busy").value){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build("button",{guild,channel,user}))
return cancel()
}
//start unpinning ticket
if (params.data == "reason"){
//unpin with reason
instance.modal(await opendiscord.builders.modals.getSafe("opendiscord:unpin-ticket-reason").build("pin-message",{guild,channel,user,ticket}))
}else{
//unpin without reason
await instance.defer("update",false)
await opendiscord.actions.get("opendiscord:unpin-ticket").run("pin-message",{guild,channel,user,ticket,reason:null,sendMessage:false})
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:unpin-message").build("pin-message",{guild,channel,user,ticket,reason:null}))
}
})
])
opendiscord.verifybars.get("opendiscord:unpin-ticket-pin-message").failure.add([
new api.ODWorker("opendiscord:back-to-pin-message",0,async (instance,params,source,cancel) => {
const {guild,channel,user} = instance
const {verifybarMessage} = params
if (!guild){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build("button",{channel,user}))
return cancel()
}
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build("button",{guild,channel,user}))
return cancel()
}
const rawReason = (verifybarMessage && verifybarMessage.embeds[0] && verifybarMessage.embeds[0].fields[0]) ? verifybarMessage.embeds[0].fields[0].value : null
const reason = (rawReason == null) ? null : rawReason.substring(3,rawReason.length-3)
await instance.update(await opendiscord.builders.messages.getSafe("opendiscord:pin-message").build("other",{guild,channel,user,ticket,reason}))
})
])
opendiscord.actions.get("opendiscord:unpin-ticket").workers.backupWorker = new api.ODWorker("opendiscord:cancel-busy",0,(instance,params) => {
//set busy to false in case of crash or cancel
params.ticket.get("opendiscord:busy").value = false
})
}
+24 -19
View File
@@ -1,15 +1,15 @@
///////////////////////////////////////
//TICKET TOPIC SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:update-ticket-priority"))
opendiscord.actions.get("opendiscord:update-ticket-priority").workers.add([
new api.ODWorker("opendiscord:update-ticket-priority",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:update-ticket-priority",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,newPriority,reason} = params
if (channel.isThread() || !(channel instanceof discord.TextChannel)) throw new api.ODSystemError("Unable to set priority of ticket! Open Ticket doesn't support threads!")
@@ -20,32 +20,37 @@ export const registerActions = async () => {
ticket.get("opendiscord:busy").value = true
if (newPriority) ticket.get("opendiscord:priority").value = newPriority.priority
//rename channel (and give error when crashed)
const pinEmoji = ticket.get("opendiscord:pinned").value ? generalConfig.data.system.pinEmoji : ""
const priorityEmoji = newPriority.channelEmoji ?? ""
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(channel.name)
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket priority update","error")
})
}catch(err){
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-priority",{guild,channel,user,originalName,newName})).message)
//calculate channel name
const channelNameResult = await opendiscord.actions.get("opendiscord:calculate-ticket-name").run("priority-change",{guild,user,option:ticket.option,channel,ticket,currentChannelName:channel.name})
if (channelNameResult && channelNameResult.shouldChangeName && typeof channelNameResult.newChannelName !== "undefined"){
const originalName = channel.name
const newName = channelNameResult.newChannelName
try{
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on priority change","error")
})
}catch(err){
opendiscord.log("Unable to rename channel while updating ticket priority! Waiting until ratelimit expires...","warning",[
{key:"oldName",value:originalName},
{key:"newName",value:newName}
])
const sentMsg = await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-priority",{guild,channel,user,originalName,newName})).message)
setTimeout(() => {if (sentMsg.deletable) sentMsg.delete()},7000) //autodelete error message
}
}
//reply with new message
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:priority-set").build(source,{guild,channel,user,ticket,priority:newPriority,reason})).message)
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:priority-set").build(origin,{guild,channel,user,ticket,priority:newPriority,reason})).message)
ticket.get("opendiscord:busy").value = false
await opendiscord.events.get("afterTicketPriorityChanged").emit([ticket,user,channel,oldPriority,newPriority,reason])
//update channel topic
await opendiscord.actions.get("opendiscord:update-ticket-topic").run("ticket-action",{guild,channel,user,ticket,sendMessage:false,newTopic:null})
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,newPriority} = params
opendiscord.log(user.displayName+" changed the priority of a ticket!","info",[
@@ -54,7 +59,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"priority",value:newPriority.id.value},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+16 -16
View File
@@ -1,16 +1,16 @@
///////////////////////////////////////
//TICKET TOPIC SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities, openticketUtils} from "../index.js"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerActions = async () => {
export async function registerActions(){
opendiscord.actions.add(new api.ODAction("opendiscord:update-ticket-topic"))
opendiscord.actions.get("opendiscord:update-ticket-topic").workers.add([
new api.ODWorker("opendiscord:update-ticket-topic",2,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:update-ticket-topic",2,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket,newTopic} = params
if (channel.isThread() || !(channel instanceof discord.TextChannel)) throw new api.ODSystemError("Unable to set topic of ticket! Open Ticket doesn't support threads!")
@@ -29,28 +29,28 @@ export const registerActions = async () => {
//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")
//reply with new message
if (params.sendMessage && newTopic) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:topic-set").build(source,{guild,channel,user,ticket,topic:newTopic})).message)
if (params.sendMessage && newTopic) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:topic-set").build(origin,{guild,channel,user,ticket,topic:newTopic})).message)
ticket.get("opendiscord:busy").value = false
if (newTopic) await opendiscord.events.get("afterTicketTopicChanged").emit([ticket,user,channel,oldTopic,newTopic])
}),
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,source,cancel) => {
new api.ODWorker("opendiscord:discord-logs",1,async (instance,params,origin,cancel) => {
const {guild,channel,user,ticket} = params
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
new api.ODWorker("opendiscord:logs",0,(instance,params,origin,cancel) => {
const {guild,channel,user,ticket,newTopic} = params
if (newTopic) opendiscord.log(user.displayName+" changed the topic of a ticket!","info",[
@@ -59,7 +59,7 @@ export const registerActions = async () => {
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"topic",value:newTopic},
{key:"method",value:source}
{key:"method",value:origin}
])
})
])
+176
View File
@@ -0,0 +1,176 @@
///////////////////////////////////////
//OPEN TICKET UTILITY FUNCTIONS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
/**Fetch the interactive message state. If not present, auto replies with error and returns `null`. When `null` is received, the worker should be returned and canceled. */
export async function replyInteractiveMessageState(instance:api.ODButtonResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",channel:discord.TextBasedChannel,message:discord.Message|null,replacementCommandName:string){
//check message state
const {user,member,guild} = instance
const interactiveMsgState = opendiscord.states.get("opendiscord:interactive-message")
if (!message){
//TODO TRANSLATION!!!
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:"Unable to locate message of interaction. Use the command `{0}` instead.".replace("{0}",replacementCommandName),layout:"simple",customTitle:"Message State Error"}))
return null
}
const state = await interactiveMsgState.getMsgState({channel,message})
if (!state){
//TODO TRANSLATION!!!
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:"This interaction is no longer valid or has expired. Use the command `{0}` instead. It is normal to receive this error after a major Open Ticket update.".replace("{0}",replacementCommandName),layout:"simple",customTitle:"Message State Expired"}))
return null
}else return state
}
/**Check the permissions for this command. If not allowed, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyHasPermissions(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",commandName:keyof api.ODGeneralJsonConfig_SystemPermissions,settings?:api.ODPermissionSettings) {
//check permissions
const {user,member,channel,guild} = instance
const generalConfig = opendiscord.configs.get("opendiscord:general")
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}))
else if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions").build(origin,{guild,channel,user,permissions:["support"]}))
return false
}else return true
}
/**Check if channel is in guild/server. If not, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyIsInGuild(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other"){
//check is in guild/server
const {user,member,channel,guild} = instance
if (!guild){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-not-in-guild").build(origin,{channel,user}))
return false
}else return true
}
/**Check if channel is valid ticket. If not, auto replies with error and returns `null`. When `null` is received, the worker should be returned and canceled. */
export async function replyIsTicket(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other") {
//check if ticket exists
const {user,member,channel,guild} = instance
if (!channel) return null
const ticket = opendiscord.tickets.get(channel.id)
if (!ticket || channel.isDMBased()){
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-unknown").build(origin,{guild,channel,user}))
return null
}else return ticket
}
/**Check if ticket is not busy and available. If not, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketIsAvailable(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket) {
//return when busy
const {user,member,channel,guild} = instance
if (ticket.get("opendiscord:busy").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-ticket-busy").build(origin,{guild,channel,user}))
return false
}else return true
}
/**Check if ticket is open. If closed, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketMustBeOpen(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket){
//return when already closed
const {user,member,channel,guild} = instance
if (ticket.get("opendiscord:closed").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.close"),layout:"simple"}))
return false
}else return true
}
/**Check if ticket is closed. If open, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketMustBeClosed(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket) {
//return when already open
const {user,member,channel,guild} = instance
if (!ticket.get("opendiscord:closed").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.reopen"),layout:"simple"}))
return false
}else return true
}
/**Check if ticket is unclaimed. If claimed, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketMustBeUnclaimed(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket) {
//return when already claimed
const {user,member,channel,guild} = instance
if (ticket.get("opendiscord:claimed").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.claim"),layout:"simple"}))
return false
}else return true
}
/**Check if ticket is claimed. If unclaimed, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketMustBeClaimed(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket) {
//return when already unclaimed
const {user,member,channel,guild} = instance
if (!ticket.get("opendiscord:claimed").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.unclaim"),layout:"simple"}))
return false
}else return true
}
/**Check if ticket is unpinned. If pinned, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketMustBeUnpinned(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket) {
//return when already pinned
const {user,member,channel,guild} = instance
if (ticket.get("opendiscord:pinned").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.pin"),layout:"simple"}))
return false
}else return true
}
/**Check if ticket is pinned. If unpinned, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyTicketMustBePinned(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance|api.ODModalResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket) {
//return when already unpinned
const {user,member,channel,guild} = instance
if (!ticket.get("opendiscord:pinned").value){
if (channel) await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(origin,{guild,channel,user,error:opendiscord.languages.getTranslation("errors.actionInvalid.unpin"),layout:"simple"}))
return false
}else return true
}
/**Check if the ticket can be closed/deleted before a message has been sent by an admin or user. If not passing the test, auto replies with error and returns `false`. When `false` is received, the worker should be returned and canceled. */
export async function replyMessageMustBeSentBeforeClose(instance:api.ODButtonResponderInstance|api.ODCommandResponderInstance|api.ODDropdownResponderInstance,origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other",ticket:api.ODTicket,commandName:keyof api.ODGeneralJsonConfig_SystemPermissions) {
//return when not allowed because of missing messages
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.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.ticketSystem.allowCloseBeforeMessage || !generalConfig.data.ticketSystem.allowCloseBeforeAdminMessage)){
const analysis = await opendiscord.transcripts.collector.ticketUserMessagesAnalysis(ticket,guild,channel)
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.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
}
return true
}else return true
}
/**Update the ticket message from a given ticket channel. This should be used after every change to the `ODTicket` after an action. */
export async function updateTicketMessage(guild:discord.Guild,channel:discord.TextBasedChannel,user:discord.User,ticket:api.ODTicket){
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
if (!ticketMessage || !user || !channel || !guild || channel.isDMBased()) return
try{
await ticketMessage.edit((await opendiscord.builders.messages.getSafe("opendiscord:ticket-message").build("other",{guild,channel,user,ticket})).message)
}catch(e){
opendiscord.log("Unable to edit ticket message!","error",[
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"messageid",value:ticketMessage.id},
{key:"option",value:ticket.option.id.value}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
+49 -70
View File
@@ -1,15 +1,37 @@
///////////////////////////////////////
//BUTTON BUILDERS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
const buttons = opendiscord.builders.buttons
const lang = opendiscord.languages
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerAllButtons = async () => {
verifybarButtons()
export async function registerAllButtons(){
//VERIFYBAR BUTTON
buttons.add(new api.ODButton("opendiscord:verifybar-button"))
buttons.get("opendiscord:verifybar-button").workers.add(
new api.ODWorker("opendiscord:verifybar-button",0,async (instance,params) => {
const {verifybar,verifyButtonId} = params
if (params.verifyButtonId.length > 40) throw new api.ODSystemError("ODButton:opendiscord:verifybar-button => verifyButtonId '"+verifyButtonId+"' exceeds 40 characters limit!")
const color = ("customColor" in params && params.customColor) ? params.customColor : "gray"
//TODO TRANSLATION!!!
const defaultLabel = (params.defaultButtonType == "✅") ? "Accept" : "Cancel"
const defaultEmoji = (params.defaultButtonType == "✅") ? "✅" : "❌"
const label = ("customLabel" in params && params.customLabel) ? params.customLabel : (params.useDefaultLabels ? defaultLabel : null)
const emoji = ("customEmoji" in params && params.customEmoji) ? params.customEmoji : defaultEmoji
instance.setCustomId("od:verifybar|"+verifybar.id.value+"|"+verifyButtonId)
instance.setMode("button")
instance.setColor(color)
if (label) instance.setLabel(label)
if (emoji) instance.setEmoji(emoji)
})
)
errorButtons()
helpMenuButtons()
panelButtons()
@@ -18,50 +40,6 @@ export const registerAllButtons = async () => {
clearButtons()
}
const verifybarButtons = () => {
//VERIFYBAR SUCCESS
buttons.add(new api.ODButton("opendiscord:verifybar-success"))
buttons.get("opendiscord:verifybar-success").workers.add(
new api.ODWorker("opendiscord:verifybar-success",0,async (instance,params) => {
const {verifybar,customData,customColor,customLabel,customEmoji} = params
if (customData && customData.length > 40) throw new api.ODSystemError("ODButton:opendiscord:verifybar-success => customData exceeds 40 characters limit!")
const newData = (customData) ? "_"+customData : ""
const newColor = customColor ?? "gray"
const newLabel = customLabel ?? ""
const newEmoji = customEmoji ?? "✅"
instance.setCustomId("od:verifybar-success_"+verifybar.id.value+newData)
instance.setMode("button")
instance.setColor(newColor)
if (newLabel) instance.setLabel(newLabel)
if (newEmoji) instance.setEmoji(newEmoji)
})
)
//VERIFYBAR FAILURE
buttons.add(new api.ODButton("opendiscord:verifybar-failure"))
buttons.get("opendiscord:verifybar-failure").workers.add(
new api.ODWorker("opendiscord:verifybar-failure",0,async (instance,params) => {
const {verifybar,customData,customColor,customLabel,customEmoji} = params
if (customData && customData.length > 40) throw new api.ODSystemError("ODButton:opendiscord:verifybar-success => customData exceeds 40 characters limit!")
const newData = (customData) ? "_"+customData : ""
const newColor = customColor ?? "gray"
const newLabel = customLabel ?? ""
const newEmoji = customEmoji ?? "❌"
instance.setCustomId("od:verifybar-failure_"+verifybar.id.value+newData)
instance.setMode("button")
instance.setColor(newColor)
if (newLabel) instance.setLabel(newLabel)
if (newEmoji) instance.setEmoji(newEmoji)
})
)
}
const errorButtons = () => {
//ERROR TICKET DEPRECATED TRANSCRIPT
buttons.add(new api.ODButton("opendiscord:error-ticket-deprecated-transcript"))
@@ -201,11 +179,11 @@ const ticketButtons = () => {
//CLOSE TICKET
buttons.add(new api.ODButton("opendiscord:close-ticket"))
buttons.get("opendiscord:close-ticket").workers.add(
new api.ODWorker("opendiscord:close-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:close-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:close-ticket_"+source)
instance.setCustomId("od:close-ticket")
instance.setColor("gray")
instance.setEmoji("🔒")
instance.setLabel(lang.getTranslation("actions.buttons.close"))
@@ -215,11 +193,11 @@ const ticketButtons = () => {
//DELETE TICKET
buttons.add(new api.ODButton("opendiscord:delete-ticket"))
buttons.get("opendiscord:delete-ticket").workers.add(
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:delete-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:delete-ticket_"+source)
instance.setCustomId("od:delete-ticket")
instance.setColor("red")
instance.setEmoji("✖")
instance.setLabel(lang.getTranslation("actions.buttons.delete"))
@@ -229,11 +207,11 @@ const ticketButtons = () => {
//REOPEN TICKET
buttons.add(new api.ODButton("opendiscord:reopen-ticket"))
buttons.get("opendiscord:reopen-ticket").workers.add(
new api.ODWorker("opendiscord:reopen-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:reopen-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:reopen-ticket_"+source)
instance.setCustomId("od:reopen-ticket")
instance.setColor("green")
instance.setEmoji("🔓")
instance.setLabel(lang.getTranslation("actions.buttons.reopen"))
@@ -243,11 +221,11 @@ const ticketButtons = () => {
//CLAIM TICKET
buttons.add(new api.ODButton("opendiscord:claim-ticket"))
buttons.get("opendiscord:claim-ticket").workers.add(
new api.ODWorker("opendiscord:claim-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:claim-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:claim-ticket_"+source)
instance.setCustomId("od:claim-ticket")
instance.setColor("green")
instance.setEmoji("👋")
instance.setLabel(lang.getTranslation("actions.buttons.claim"))
@@ -257,11 +235,11 @@ const ticketButtons = () => {
//UNCLAIM TICKET
buttons.add(new api.ODButton("opendiscord:unclaim-ticket"))
buttons.get("opendiscord:unclaim-ticket").workers.add(
new api.ODWorker("opendiscord:unclaim-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:unclaim-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:unclaim-ticket_"+source)
instance.setCustomId("od:unclaim-ticket")
instance.setColor("green")
instance.setEmoji("↩️")
instance.setLabel(lang.getTranslation("actions.buttons.unclaim"))
@@ -271,13 +249,13 @@ const ticketButtons = () => {
//PIN TICKET
buttons.add(new api.ODButton("opendiscord:pin-ticket"))
buttons.get("opendiscord:pin-ticket").workers.add(
new api.ODWorker("opendiscord:pin-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:pin-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:pin-ticket_"+source)
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"))
})
)
@@ -285,13 +263,13 @@ const ticketButtons = () => {
//UNPIN TICKET
buttons.add(new api.ODButton("opendiscord:unpin-ticket"))
buttons.get("opendiscord:unpin-ticket").workers.add(
new api.ODWorker("opendiscord:unpin-ticket",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:unpin-ticket",0,async (instance,params,origin) => {
const {guild,channel,ticket} = params
instance.setMode("button")
instance.setCustomId("od:unpin-ticket_"+source)
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"))
})
)
@@ -301,7 +279,7 @@ const transcriptButtons = () => {
//TRANSCRIPT HTML VISIT
buttons.add(new api.ODButton("opendiscord:transcript-html-visit"))
buttons.get("opendiscord:transcript-html-visit").workers.add(
new api.ODWorker("opendiscord:transcript-html-visit",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transcript-html-visit",0,async (instance,params,origin) => {
const {result} = params
instance.setMode("url")
if (result.data) instance.setUrl(result.data.url)
@@ -314,9 +292,9 @@ const transcriptButtons = () => {
//TRANSCRIPT ERROR RETRY
buttons.add(new api.ODButton("opendiscord:transcript-error-retry"))
buttons.get("opendiscord:transcript-error-retry").workers.add(
new api.ODWorker("opendiscord:transcript-error-retry",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transcript-error-retry",0,async (instance,params,origin) => {
instance.setMode("button")
instance.setCustomId("od:transcript-error-retry_"+source)
instance.setCustomId("od:transcript-error-retry_"+origin)
instance.setColor("gray")
instance.setEmoji("🔄")
instance.setLabel(lang.getTranslation("transcripts.errors.retry"))
@@ -326,9 +304,9 @@ const transcriptButtons = () => {
//TRANSCRIPT ERROR CONTINUE
buttons.add(new api.ODButton("opendiscord:transcript-error-continue"))
buttons.get("opendiscord:transcript-error-continue").workers.add(
new api.ODWorker("opendiscord:transcript-error-continue",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transcript-error-continue",0,async (instance,params,origin) => {
instance.setMode("button")
instance.setCustomId("od:transcript-error-continue_"+source)
instance.setCustomId("od:transcript-error-continue_"+origin)
instance.setColor("red")
instance.setEmoji("✖")
instance.setLabel(lang.getTranslation("transcripts.errors.continue"))
@@ -340,12 +318,13 @@ const clearButtons = () => {
//CLEAR CONTINUE
buttons.add(new api.ODButton("opendiscord:clear-continue"))
buttons.get("opendiscord:clear-continue").workers.add(
new api.ODWorker("opendiscord:clear-continue",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:clear-continue",0,async (instance,params,origin) => {
instance.setMode("button")
instance.setCustomId("od:clear-continue_"+source+"_"+params.filter)
instance.setCustomId("od:clear-continue")
instance.setColor("red")
instance.setEmoji("✖")
instance.setLabel(lang.getTranslation("actions.buttons.clear"))
instance.setDisabled(params.inProgress)
})
)
}
+2 -2
View File
@@ -1,12 +1,12 @@
///////////////////////////////////////
//DROPDOWN BUILDERS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
const dropdowns = opendiscord.builders.dropdowns
export const registerAllDropdowns = async () => {
export async function registerAllDropdowns(){
panelDropdowns()
}
+169 -145
View File
@@ -1,7 +1,7 @@
///////////////////////////////////////
//EMBED BUILDERS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
import nodepath from "path"
@@ -9,7 +9,7 @@ const embeds = opendiscord.builders.embeds
const lang = opendiscord.languages
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerAllEmbeds = async () => {
export async function registerAllEmbeds(){
errorEmbeds()
helpMenuEmbeds()
statsEmbeds()
@@ -23,12 +23,12 @@ export const registerAllEmbeds = async () => {
extraEmbeds()
}
/**Utility function to get the translated "method" from the source. Mostly used in error embeds. */
const getMethodFromSource = (source:"slash"|"text"|"button"|"dropdown"|"modal"|"other"): string => {
if (source == "slash" || source == "text") return lang.getTranslation("params.lowercase.command")
else if (source == "button") return lang.getTranslation("params.lowercase.button")
else if (source == "dropdown") return lang.getTranslation("params.lowercase.dropdown")
else if (source == "modal") return lang.getTranslation("params.lowercase.modal")
/**Utility function to get the translated "method" from the origin. Mostly used in error embeds. */
const getMethodFromOrigin = (origin:"slash"|"text"|"button"|"dropdown"|"modal"|"other"): string => {
if (origin == "slash" || origin == "text") return lang.getTranslation("params.lowercase.command")
else if (origin == "button") return lang.getTranslation("params.lowercase.button")
else if (origin == "dropdown") return lang.getTranslation("params.lowercase.dropdown")
else if (origin == "modal") return lang.getTranslation("params.lowercase.modal")
else return lang.getTranslation("params.lowercase.method")
}
//lang.getTranslation()
@@ -37,12 +37,12 @@ const errorEmbeds = () => {
//ERROR
embeds.add(new api.ODEmbed("opendiscord:error"))
embeds.get("opendiscord:error").workers.add(
new api.ODWorker("opendiscord:error",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error",0,async (instance,params,origin) => {
const {user,error,layout,customTitle} = params
const method = getMethodFromSource(source)
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"))
@@ -154,10 +154,10 @@ const errorEmbeds = () => {
//ERROR NO PERMISSIONS
embeds.add(new api.ODEmbed("opendiscord:error-no-permissions"))
embeds.get("opendiscord:error-no-permissions").workers.add(
new api.ODWorker("opendiscord:error-no-permissions",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-no-permissions",0,async (instance,params,origin) => {
const {user,permissions} = params
const method = getMethodFromSource(source)
const method = getMethodFromOrigin(origin)
const renderedPerms = permissions.map((perm) => {
if (perm == "developer") return "- "+lang.getTranslation("errors.permissions.developer")
@@ -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]))
@@ -180,12 +180,12 @@ const errorEmbeds = () => {
//ERROR NO PERMISSIONS COOLDOWN
embeds.add(new api.ODEmbed("opendiscord:error-no-permissions-cooldown"))
embeds.get("opendiscord:error-no-permissions-cooldown").workers.add(
new api.ODWorker("opendiscord:error-no-permissions-cooldown",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-no-permissions-cooldown",0,async (instance,params,origin) => {
const {user} = params
const method = getMethodFromSource(source)
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]))
@@ -196,12 +196,12 @@ const errorEmbeds = () => {
//ERROR NO PERMISSIONS BLACKLISTED
embeds.add(new api.ODEmbed("opendiscord:error-no-permissions-blacklisted"))
embeds.get("opendiscord:error-no-permissions-blacklisted").workers.add(
new api.ODWorker("opendiscord:error-no-permissions-blacklisted",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-no-permissions-blacklisted",0,async (instance,params,origin) => {
const {user} = params
const method = getMethodFromSource(source)
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]))
@@ -211,10 +211,10 @@ const errorEmbeds = () => {
//ERROR NO PERMISSIONS LIMITS
embeds.add(new api.ODEmbed("opendiscord:error-no-permissions-limits"))
embeds.get("opendiscord:error-no-permissions-limits").workers.add(
new api.ODWorker("opendiscord:error-no-permissions-limits",0,async (instance,params,source) => {
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"))
@@ -227,12 +227,12 @@ const errorEmbeds = () => {
//ERROR RESPONDER TIMEOUT
embeds.add(new api.ODEmbed("opendiscord:error-responder-timeout"))
embeds.get("opendiscord:error-responder-timeout").workers.add(
new api.ODWorker("opendiscord:error-responder-timeout",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-responder-timeout",0,async (instance,params,origin) => {
const {user} = params
const method = getMethodFromSource(source)
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]))
@@ -244,10 +244,10 @@ const errorEmbeds = () => {
//ERROR TICKET UNKNOWN
embeds.add(new api.ODEmbed("opendiscord:error-ticket-unknown"))
embeds.get("opendiscord:error-ticket-unknown").workers.add(
new api.ODWorker("opendiscord:error-ticket-unknown",0,async (instance,params,source) => {
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"))
@@ -258,10 +258,10 @@ const errorEmbeds = () => {
//ERROR TICKET DEPRECATED
embeds.add(new api.ODEmbed("opendiscord:error-ticket-deprecated"))
embeds.get("opendiscord:error-ticket-deprecated").workers.add(
new api.ODWorker("opendiscord:error-ticket-deprecated",0,async (instance,params,source) => {
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"))
@@ -272,7 +272,7 @@ const errorEmbeds = () => {
//ERROR OPTION UNKNOWN
embeds.add(new api.ODEmbed("opendiscord:error-option-unknown"))
embeds.get("opendiscord:error-option-unknown").workers.add(
new api.ODWorker("opendiscord:error-option-unknown",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-option-unknown",0,async (instance,params,origin) => {
const {user} = params
const renderedTicketOptions = opendiscord.options.getAll().map((option) => {
@@ -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"))
@@ -292,7 +292,7 @@ const errorEmbeds = () => {
//ERROR PANEL UNKNOWN
embeds.add(new api.ODEmbed("opendiscord:error-panel-unknown"))
embeds.get("opendiscord:error-panel-unknown").workers.add(
new api.ODWorker("opendiscord:error-panel-unknown",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-panel-unknown",0,async (instance,params,origin) => {
const {user} = params
const renderedPanels = opendiscord.panels.getAll().map((panel) => {
@@ -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})
@@ -312,12 +312,12 @@ const errorEmbeds = () => {
//ERROR NOT IN GUILD
embeds.add(new api.ODEmbed("opendiscord:error-not-in-guild"))
embeds.get("opendiscord:error-not-in-guild").workers.add(
new api.ODWorker("opendiscord:error-not-in-guild",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-not-in-guild",0,async (instance,params,origin) => {
const {user} = params
const method = getMethodFromSource(source)
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]))
@@ -327,19 +327,43 @@ const errorEmbeds = () => {
//ERROR CHANNEL RENAME
embeds.add(new api.ODEmbed("opendiscord:error-channel-rename"))
embeds.get("opendiscord:error-channel-rename").workers.add(
new api.ODWorker("opendiscord:error-channel-rename",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-channel-rename",0,async (instance,params,origin) => {
const {channel,user,originalName,newName} = params
const method = (source == "ticket-move" || source == "ticket-pin" || source == "ticket-rename" || source == "ticket-unpin" || source == "ticket-priority" || source == "ticket-transfer") ? source : getMethodFromSource(source)
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"))
instance.setFooter(lang.getTranslationWithParams("errors.descriptions.channelRenameSource",[method]))
instance.addFields(
{name:lang.getTranslation("params.uppercase.originalName")+":",value:"```#"+originalName+"```",inline:false},
{name:lang.getTranslation("params.uppercase.newName")+":",value:"```#"+newName+"```",inline:false}
{name:lang.getTranslation("params.uppercase.originalName")+":",value:"```#"+originalName+"```",inline:true},
{name:lang.getTranslation("params.uppercase.newName")+":",value:"```#"+newName+"```",inline:true}
)
})
)
//ERROR CHANNEL CATEGORY
embeds.add(new api.ODEmbed("opendiscord:error-channel-category"))
embeds.get("opendiscord:error-channel-category").workers.add(
new api.ODWorker("opendiscord:error-channel-category",0,async (instance,params,origin) => {
const {channel,user,originalCategory,newCategory} = params
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.ticketSystem.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
//TODO TRANSLATION!!!
instance.setTitle(utilities.emojiTitle("❌","Unable To Change Category"))
instance.setAuthor(user.displayName,user.displayAvatarURL())
//TODO TRANSLATION!!!
instance.setDescription("Due to Discord rate limits, the channel category could not be changed immediately. It will be changed automatically within 10 minutes if the bot remains online.")
instance.setFooter(lang.getTranslationWithParams("errors.descriptions.channelRenameSource",[method]))
instance.addFields(
//TODO TRANSLATION!!!
{name:"Original Category"+":",value:"```"+originalCategory+"```",inline:true},
//TODO TRANSLATION!!!
{name:"New Category"+":",value:"```"+newCategory+"```",inline:true}
)
})
)
@@ -347,12 +371,12 @@ const errorEmbeds = () => {
//ERROR TICKET BUSY
embeds.add(new api.ODEmbed("opendiscord:error-ticket-busy"))
embeds.get("opendiscord:error-ticket-busy").workers.add(
new api.ODWorker("opendiscord:error-ticket-busy",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:error-ticket-busy",0,async (instance,params,origin) => {
const {user} = params
const method = getMethodFromSource(source)
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]))
@@ -387,7 +411,7 @@ const statsEmbeds = () => {
new api.ODWorker("opendiscord:stats-global",0,async (instance,params) => {
const {guild,channel,user} = params
const scope = opendiscord.stats.get("opendiscord:global")
const scope = opendiscord.statistics.get("opendiscord:global")
if (!scope) return
const data = await scope.render("GLOBAL",guild,channel,user)
@@ -397,7 +421,7 @@ const statsEmbeds = () => {
if (opendiscord.permissions.hasPermissions("owner",await opendiscord.permissions.getPermissions(user,channel,guild))){
//show system data when owner or developer
const systemScope = opendiscord.stats.get("opendiscord:system")
const systemScope = opendiscord.statistics.get("opendiscord:system")
if (!systemScope) return
const systemData = await systemScope.render("GLOBAL",guild,channel,user)
instance.addFields({name:systemScope.name,value:systemData,inline:false})
@@ -411,9 +435,9 @@ const statsEmbeds = () => {
new api.ODWorker("opendiscord:stats-ticket",0,async (instance,params) => {
const {guild,channel,user,scopeData} = params
const scope = opendiscord.stats.get("opendiscord:ticket")
const participantsScope = opendiscord.stats.get("opendiscord:participants")
const messagesScope = opendiscord.stats.get("opendiscord:messages")
const scope = opendiscord.statistics.get("opendiscord:ticket")
const participantsScope = opendiscord.statistics.get("opendiscord:participants")
const messagesScope = opendiscord.statistics.get("opendiscord:messages")
if (!scope || !participantsScope || !messagesScope) return
const data = await scope.render(scopeData.id.value,guild,channel,user)
const participantsData = await participantsScope.render(scopeData.id.value,guild,channel,user)
@@ -433,7 +457,7 @@ const statsEmbeds = () => {
new api.ODWorker("opendiscord:stats-user",0,async (instance,params) => {
const {guild,channel,user,scopeData} = params
const scope = opendiscord.stats.get("opendiscord:user")
const scope = opendiscord.statistics.get("opendiscord:user")
if (!scope) return
const data = await scope.render(scopeData.id,guild,channel,user)
@@ -477,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)]))
@@ -510,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){
@@ -529,7 +553,7 @@ const ticketEmbeds = () => {
//TICKET CREATED
embeds.add(new api.ODEmbed("opendiscord:ticket-created"))
embeds.get("opendiscord:ticket-created").workers.add(
new api.ODWorker("opendiscord:ticket-created",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:ticket-created",0,async (instance,params,origin) => {
const {user} = params
instance.setColor(generalConfig.data.mainColor)
@@ -543,7 +567,7 @@ const ticketEmbeds = () => {
//TICKET CREATED DM
embeds.add(new api.ODEmbed("opendiscord:ticket-created-dm"))
embeds.get("opendiscord:ticket-created-dm").workers.add(
new api.ODWorker("opendiscord:ticket-created-dm",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:ticket-created-dm",0,async (instance,params,origin) => {
const {user,ticket} = params
const embedOptions = ticket.option.get("opendiscord:dm-message-embed").value
@@ -563,10 +587,10 @@ const ticketEmbeds = () => {
//TICKET CREATED LOGS
embeds.add(new api.ODEmbed("opendiscord:ticket-created-logs"))
embeds.get("opendiscord:ticket-created-logs").workers.add(
new api.ODWorker("opendiscord:ticket-created-logs",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:ticket-created-logs",0,async (instance,params,origin) => {
const {user,ticket} = params
const method = (source == "panel-button" || source == "panel-dropdown") ? lang.getTranslation("params.uppercase.panel") : (source == "slash" || source == "text") ? lang.getTranslation("params.uppercase.command") : lang.getTranslation("params.uppercase.system")
const method = (origin == "panel-button" || origin == "panel-dropdown") ? lang.getTranslation("params.uppercase.panel") : (origin == "slash" || origin == "text") ? lang.getTranslation("params.uppercase.command") : lang.getTranslation("params.uppercase.system")
const blacklisted = opendiscord.blacklist.exists(user.id) ? lang.getTranslation("params.uppercase.true") : lang.getTranslation("params.uppercase.false")
instance.setColor(generalConfig.data.mainColor)
@@ -586,7 +610,7 @@ const ticketEmbeds = () => {
//TICKET MESSAGE
embeds.add(new api.ODEmbed("opendiscord:ticket-message"))
embeds.get("opendiscord:ticket-message").workers.add(
new api.ODWorker("opendiscord:ticket-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:ticket-message",0,async (instance,params,origin) => {
const {user,ticket} = params
const embedOptions = ticket.option.get("opendiscord:ticket-message-embed").value
@@ -599,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+"```",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){
@@ -625,161 +649,161 @@ const ticketEmbeds = () => {
//TICKET CLOSED
embeds.add(new api.ODEmbed("opendiscord:close-message"))
embeds.get("opendiscord:close-message").workers.add(
new api.ODWorker("opendiscord:close-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:close-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET REOPENED
embeds.add(new api.ODEmbed("opendiscord:reopen-message"))
embeds.get("opendiscord:reopen-message").workers.add(
new api.ODWorker("opendiscord:reopen-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:reopen-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET DELETED
embeds.add(new api.ODEmbed("opendiscord:delete-message"))
embeds.get("opendiscord:delete-message").workers.add(
new api.ODWorker("opendiscord:delete-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:delete-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET CLAIMED
embeds.add(new api.ODEmbed("opendiscord:claim-message"))
embeds.get("opendiscord:claim-message").workers.add(
new api.ODWorker("opendiscord:claim-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:claim-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET UNCLAIMED
embeds.add(new api.ODEmbed("opendiscord:unclaim-message"))
embeds.get("opendiscord:unclaim-message").workers.add(
new api.ODWorker("opendiscord:unclaim-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:unclaim-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET PINNED
embeds.add(new api.ODEmbed("opendiscord:pin-message"))
embeds.get("opendiscord:pin-message").workers.add(
new api.ODWorker("opendiscord:pin-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:pin-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
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 ?? "/")+"```"})
})
)
//TICKET UNPINNED
embeds.add(new api.ODEmbed("opendiscord:unpin-message"))
embeds.get("opendiscord:unpin-message").workers.add(
new api.ODWorker("opendiscord:unpin-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:unpin-message",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
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 ?? "/")+"```"})
})
)
//TICKET RENAMED
embeds.add(new api.ODEmbed("opendiscord:rename-message"))
embeds.get("opendiscord:rename-message").workers.add(
new api.ODWorker("opendiscord:rename-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:rename-message",0,async (instance,params,origin) => {
const {user,ticket,reason,data} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET MOVED
embeds.add(new api.ODEmbed("opendiscord:move-message"))
embeds.get("opendiscord:move-message").workers.add(
new api.ODWorker("opendiscord:move-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:move-message",0,async (instance,params,origin) => {
const {user,ticket,reason,data} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET USER ADDED
embeds.add(new api.ODEmbed("opendiscord:add-message"))
embeds.get("opendiscord:add-message").workers.add(
new api.ODWorker("opendiscord:add-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:add-message",0,async (instance,params,origin) => {
const {user,ticket,reason,data} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET USER REMOVED
embeds.add(new api.ODEmbed("opendiscord:remove-message"))
embeds.get("opendiscord:remove-message").workers.add(
new api.ODWorker("opendiscord:remove-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:remove-message",0,async (instance,params,origin) => {
const {user,ticket,reason,data} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//TICKET ACTION DM
embeds.add(new api.ODEmbed("opendiscord:ticket-action-dm"))
embeds.get("opendiscord:ticket-action-dm").workers.add(
new api.ODWorker("opendiscord:ticket-action-dm",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:ticket-action-dm",0,async (instance,params,origin) => {
const {user,mode,ticket,reason,additionalData} = params
const channel = await opendiscord.tickets.getTicketChannel(ticket)
@@ -804,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")))
@@ -828,7 +852,7 @@ const ticketEmbeds = () => {
//TICKET ACTION LOGS
embeds.add(new api.ODEmbed("opendiscord:ticket-action-logs"))
embeds.get("opendiscord:ticket-action-logs").workers.add(
new api.ODWorker("opendiscord:ticket-action-logs",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:ticket-action-logs",0,async (instance,params,origin) => {
const {user,mode,ticket,reason,additionalData} = params
const channel = await opendiscord.tickets.getTicketChannel(ticket)
@@ -840,7 +864,7 @@ const ticketEmbeds = () => {
{name:lang.getTranslation("params.uppercase.ticket")+":",value:"```#"+(channel ? channel.name : "<unknown>")+"```",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")))
@@ -858,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")))
@@ -884,7 +908,7 @@ const blacklistEmbeds = () => {
//BLACKLIST VIEW
embeds.add(new api.ODEmbed("opendiscord:blacklist-view"))
embeds.get("opendiscord:blacklist-view").workers.add(
new api.ODWorker("opendiscord:blacklist-view",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:blacklist-view",0,async (instance,params,origin) => {
const {user} = params
const renderedUsers: string[] = []
@@ -904,7 +928,7 @@ const blacklistEmbeds = () => {
//BLACKLIST GET
embeds.add(new api.ODEmbed("opendiscord:blacklist-get"))
embeds.get("opendiscord:blacklist-get").workers.add(
new api.ODWorker("opendiscord:blacklist-get",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:blacklist-get",0,async (instance,params,origin) => {
const {user,data} = params
const blacklist = opendiscord.blacklist.get(data.id)
@@ -914,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)])+"*")
})
@@ -923,35 +947,35 @@ const blacklistEmbeds = () => {
//BLACKLIST ADD
embeds.add(new api.ODEmbed("opendiscord:blacklist-add"))
embeds.get("opendiscord:blacklist-add").workers.add(
new api.ODWorker("opendiscord:blacklist-add",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:blacklist-add",0,async (instance,params,origin) => {
const {user,data,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//BLACKLIST REMOVE
embeds.add(new api.ODEmbed("opendiscord:blacklist-remove"))
embeds.get("opendiscord:blacklist-remove").workers.add(
new api.ODWorker("opendiscord:blacklist-remove",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:blacklist-remove",0,async (instance,params,origin) => {
const {user,data,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//BLACKLIST DM
embeds.add(new api.ODEmbed("opendiscord:blacklist-dm"))
embeds.get("opendiscord:blacklist-dm").workers.add(
new api.ODWorker("opendiscord:blacklist-dm",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:blacklist-dm",0,async (instance,params,origin) => {
const {user,mode,data,reason} = params
const title = (mode == "add") ? lang.getTranslation("actions.titles.blacklistAddDm") : lang.getTranslation("actions.titles.blacklistRemoveDm")
@@ -961,14 +985,14 @@ 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 ?? "/")+"```"})
})
)
//BLACKLIST LOGS
embeds.add(new api.ODEmbed("opendiscord:blacklist-logs"))
embeds.get("opendiscord:blacklist-logs").workers.add(
new api.ODWorker("opendiscord:blacklist-logs",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:blacklist-logs",0,async (instance,params,origin) => {
const {user,mode,data,reason} = params
const title = (mode == "add") ? lang.getTranslation("actions.titles.blacklistAdd") : lang.getTranslation("actions.titles.blacklistRemove")
@@ -980,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 ?? "/")+"```"})
})
)
}
@@ -989,7 +1013,7 @@ const transcriptEmbeds = () => {
//TRANSCRIPT TEXT READY
embeds.add(new api.ODEmbed("opendiscord:transcript-text-ready"))
embeds.get("opendiscord:transcript-text-ready").workers.add(
new api.ODWorker("opendiscord:transcript-text-ready",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transcript-text-ready",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,compiler} = params
const transcriptConfig = opendiscord.configs.get("opendiscord:transcripts")
@@ -1007,11 +1031,11 @@ const transcriptEmbeds = () => {
}catch{}
}
if (source == "channel") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdChannel",[lang.getTranslation("params.lowercase.text")]))
else if (source == "creator-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdCreator",[lang.getTranslation("params.lowercase.text")]))
else if (source == "participant-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdParticipant",[lang.getTranslation("params.lowercase.text")]))
else if (source == "active-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdActiveAdmin",[lang.getTranslation("params.lowercase.text")]))
else if (source == "every-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdEveryAdmin",[lang.getTranslation("params.lowercase.text")]))
if (origin == "channel") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdChannel",[lang.getTranslation("params.lowercase.text")]))
else if (origin == "creator-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdCreator",[lang.getTranslation("params.lowercase.text")]))
else if (origin == "participant-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdParticipant",[lang.getTranslation("params.lowercase.text")]))
else if (origin == "active-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdActiveAdmin",[lang.getTranslation("params.lowercase.text")]))
else if (origin == "every-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdEveryAdmin",[lang.getTranslation("params.lowercase.text")]))
else instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdOther",[lang.getTranslation("params.lowercase.text")]))
})
)
@@ -1019,7 +1043,7 @@ const transcriptEmbeds = () => {
//TRANSCRIPT HTML READY
embeds.add(new api.ODEmbed("opendiscord:transcript-html-ready"))
embeds.get("opendiscord:transcript-html-ready").workers.add(
new api.ODWorker("opendiscord:transcript-html-ready",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transcript-html-ready",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,compiler,result} = params
const transcriptConfig = opendiscord.configs.get("opendiscord:transcripts")
@@ -1038,11 +1062,11 @@ const transcriptEmbeds = () => {
}catch{}
}
if (source == "channel") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdChannel",[lang.getTranslation("params.lowercase.html")]))
else if (source == "creator-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdCreator",[lang.getTranslation("params.lowercase.html")]))
else if (source == "participant-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdParticipant",[lang.getTranslation("params.lowercase.html")]))
else if (source == "active-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdActiveAdmin",[lang.getTranslation("params.lowercase.html")]))
else if (source == "every-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdEveryAdmin",[lang.getTranslation("params.lowercase.html")]))
if (origin == "channel") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdChannel",[lang.getTranslation("params.lowercase.html")]))
else if (origin == "creator-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdCreator",[lang.getTranslation("params.lowercase.html")]))
else if (origin == "participant-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdParticipant",[lang.getTranslation("params.lowercase.html")]))
else if (origin == "active-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdActiveAdmin",[lang.getTranslation("params.lowercase.html")]))
else if (origin == "every-admin-dm") instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdEveryAdmin",[lang.getTranslation("params.lowercase.html")]))
else instance.setDescription(lang.getTranslationWithParams("transcripts.success.createdOther",[lang.getTranslation("params.lowercase.html")]))
})
)
@@ -1050,7 +1074,7 @@ const transcriptEmbeds = () => {
//TRANSCRIPT HTML PROGRESS
embeds.add(new api.ODEmbed("opendiscord:transcript-html-progress"))
embeds.get("opendiscord:transcript-html-progress").workers.add(
new api.ODWorker("opendiscord:transcript-html-progress",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transcript-html-progress",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,compiler,remaining} = params
const remainingDate = new Date(new Date().getTime()+remaining)
@@ -1076,15 +1100,15 @@ const transcriptEmbeds = () => {
//TRANSCRIPT ERROR
embeds.add(new api.ODEmbed("opendiscord:transcript-error"))
embeds.get("opendiscord:transcript-error").workers.add(
new api.ODWorker("opendiscord:transcript-error",0,async (instance,params,source) => {
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 ?? "/")+"```"})
})
)
}
@@ -1093,7 +1117,7 @@ const roleEmbeds = () => {
//REACTION ROLE
embeds.add(new api.ODEmbed("opendiscord:reaction-role"))
embeds.get("opendiscord:reaction-role").workers.add(
new api.ODWorker("opendiscord:reaction-role",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:reaction-role",0,async (instance,params,origin) => {
const {guild,user,role,result} = params
instance.setColor(generalConfig.data.mainColor)
@@ -1117,7 +1141,7 @@ const roleEmbeds = () => {
//REACTION ROLE DM
embeds.add(new api.ODEmbed("opendiscord:reaction-role-dm"))
embeds.get("opendiscord:reaction-role-dm").workers.add(
new api.ODWorker("opendiscord:reaction-role-dm",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:reaction-role-dm",0,async (instance,params,origin) => {
const {guild,user,role,result} = params
instance.setColor(generalConfig.data.mainColor)
@@ -1143,7 +1167,7 @@ const roleEmbeds = () => {
//REACTION ROLE LOGS
embeds.add(new api.ODEmbed("opendiscord:reaction-role-logs"))
embeds.get("opendiscord:reaction-role-logs").workers.add(
new api.ODWorker("opendiscord:reaction-role-logs",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:reaction-role-logs",0,async (instance,params,origin) => {
const {guild,user,role,result} = params
instance.setColor(generalConfig.data.mainColor)
@@ -1171,7 +1195,7 @@ const clearEmbeds = () => {
//CLEAR VERIFY MESSAGE
embeds.add(new api.ODEmbed("opendiscord:clear-verify-message"))
embeds.get("opendiscord:clear-verify-message").workers.add(
new api.ODWorker("opendiscord:clear-verify-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:clear-verify-message",0,async (instance,params,origin) => {
const {guild,channel,user,filter,list} = params
instance.setColor(generalConfig.data.mainColor)
@@ -1189,7 +1213,7 @@ const clearEmbeds = () => {
//CLEAR MESSAGE
embeds.add(new api.ODEmbed("opendiscord:clear-message"))
embeds.get("opendiscord:clear-message").workers.add(
new api.ODWorker("opendiscord:clear-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:clear-message",0,async (instance,params,origin) => {
const {guild,channel,user,filter,list} = params
instance.setColor(generalConfig.data.mainColor)
@@ -1206,7 +1230,7 @@ const clearEmbeds = () => {
//CLEAR LOGS
embeds.add(new api.ODEmbed("opendiscord:clear-logs"))
embeds.get("opendiscord:clear-logs").workers.add(
new api.ODWorker("opendiscord:clear-logs",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:clear-logs",0,async (instance,params,origin) => {
const {guild,channel,user,filter,list} = params
instance.setColor(generalConfig.data.mainColor)
@@ -1226,10 +1250,10 @@ const autoEmbeds = () => {
//AUTOCLOSE MESSAGE
embeds.add(new api.ODEmbed("opendiscord:autoclose-message"))
embeds.get("opendiscord:autoclose-message").workers.add(
new api.ODWorker("opendiscord:autoclose-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:autoclose-message",0,async (instance,params,origin) => {
const {user,ticket} = params
const hours: number = ticket.get("opendiscord:autoclose-hours").value
const description = (source == "leave") ? lang.getTranslation("actions.descriptions.autocloseLeave") : lang.getTranslationWithParams("actions.descriptions.autocloseTimeout",[hours.toString()])
const description = (origin == "leave") ? lang.getTranslation("actions.descriptions.autocloseLeave") : lang.getTranslationWithParams("actions.descriptions.autocloseTimeout",[hours.toString()])
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
@@ -1242,10 +1266,10 @@ const autoEmbeds = () => {
//AUTODELETE MESSAGE
embeds.add(new api.ODEmbed("opendiscord:autodelete-message"))
embeds.get("opendiscord:autodelete-message").workers.add(
new api.ODWorker("opendiscord:autodelete-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:autodelete-message",0,async (instance,params,origin) => {
const {user,ticket} = params
const days: number = ticket.get("opendiscord:autodelete-days").value
const description = (source == "leave") ? lang.getTranslation("actions.descriptions.autodeleteLeave") : lang.getTranslationWithParams("actions.descriptions.autodeleteTimeout",[days.toString()])
const description = (origin == "leave") ? lang.getTranslation("actions.descriptions.autodeleteLeave") : lang.getTranslationWithParams("actions.descriptions.autodeleteTimeout",[days.toString()])
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
@@ -1258,56 +1282,56 @@ const autoEmbeds = () => {
//AUTOCLOSE ENABLE
embeds.add(new api.ODEmbed("opendiscord:autoclose-enable"))
embeds.get("opendiscord:autoclose-enable").workers.add(
new api.ODWorker("opendiscord:autoclose-enable",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:autoclose-enable",0,async (instance,params,origin) => {
const {user,ticket,reason,time} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//AUTODELETE ENABLE
embeds.add(new api.ODEmbed("opendiscord:autodelete-enable"))
embeds.get("opendiscord:autodelete-enable").workers.add(
new api.ODWorker("opendiscord:autodelete-enable",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:autodelete-enable",0,async (instance,params,origin) => {
const {user,ticket,reason,time} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//AUTOCLOSE DISABLE
embeds.add(new api.ODEmbed("opendiscord:autoclose-disable"))
embeds.get("opendiscord:autoclose-disable").workers.add(
new api.ODWorker("opendiscord:autoclose-disable",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:autoclose-disable",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//AUTODELETE DISABLE
embeds.add(new api.ODEmbed("opendiscord:autodelete-disable"))
embeds.get("opendiscord:autodelete-disable").workers.add(
new api.ODWorker("opendiscord:autodelete-disable",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:autodelete-disable",0,async (instance,params,origin) => {
const {user,ticket,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
}
@@ -1316,7 +1340,7 @@ const extraEmbeds = () => {
//TOPIC SET
embeds.add(new api.ODEmbed("opendiscord:topic-set"))
embeds.get("opendiscord:topic-set").workers.add(
new api.ODWorker("opendiscord:topic-set",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:topic-set",0,async (instance,params,origin) => {
const {user,topic} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
@@ -1330,21 +1354,21 @@ const extraEmbeds = () => {
//PRIORITY SET
embeds.add(new api.ODEmbed("opendiscord:priority-set"))
embeds.get("opendiscord:priority-set").workers.add(
new api.ODWorker("opendiscord:priority-set",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:priority-set",0,async (instance,params,origin) => {
const {user,priority,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
//PRIORITY GET
embeds.add(new api.ODEmbed("opendiscord:priority-get"))
embeds.get("opendiscord:priority-get").workers.add(
new api.ODWorker("opendiscord:priority-get",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:priority-get",0,async (instance,params,origin) => {
const {user,priority} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
@@ -1357,14 +1381,14 @@ const extraEmbeds = () => {
//TRANSFER MESSAGE
embeds.add(new api.ODEmbed("opendiscord:transfer-message"))
embeds.get("opendiscord:transfer-message").workers.add(
new api.ODWorker("opendiscord:transfer-message",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:transfer-message",0,async (instance,params,origin) => {
const {user,oldCreator,newCreator,reason} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
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 ?? "/")+"```"})
})
)
}
+3 -3
View File
@@ -1,14 +1,14 @@
///////////////////////////////////////
//FILE BUILDERS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import {opendiscord, api, utilities} from "../index.js"
import * as discord from "discord.js"
const files = opendiscord.builders.files
const lang = opendiscord.languages
const transcriptConfig = opendiscord.configs.get("opendiscord:transcripts")
export const registerAllFiles = async () => {
export async function registerAllFiles(){
transcriptFiles()
}
@@ -17,7 +17,7 @@ const transcriptFiles = () => {
//TEXT TRANSCRIPT
files.add(new api.ODFile("opendiscord:text-transcript"))
files.get("opendiscord:text-transcript").workers.add(
new api.ODWorker("opendiscord:text-transcript",0,async (instance,params,source) => {
new api.ODWorker("opendiscord:text-transcript",0,async (instance,params,origin) => {
const {guild,channel,user,ticket,compiler,result} = params
const fileMode = transcriptConfig.data.textTranscriptStyle.fileMode

Some files were not shown because too many files have changed in this diff Show More