Compare commits

...
93 Commits
Author SHA1 Message Date
Jasper 16f1be32a3 Open Ticket v4.1.0
Open Ticket v4.1.0
2025-11-09 19:11:57 +01:00
DJj123dj e1a6feea6e Updated README.md language list 2025-11-09 19:05:53 +01:00
DJj123dj 9668851a82 Added 9 updated translations for v4.1 2025-11-09 13:04:51 +01:00
DJj123dj 68d6a2df3f Added language merge utility tool 2025-11-09 12:51:18 +01:00
DJj123dj f835850b3f Translated code strings using language manager 2025-11-09 12:50:57 +01:00
Jasper f7fa0998b6 v4.1 Minor Bugfixes
v4.1 Minor Bugfixes
2025-11-05 14:55:05 +01:00
JasperAtSchool 4416736947 Merge branch 'open-discord-bots:dev-v4.1.0' into dev-v4.1.0 2025-11-05 14:53:43 +01:00
JasperAtSchool 5494c1ad8f (v4.1) Minor Bugfixes 2025-11-05 14:53:05 +01:00
DJj123dj 4147658575 README Updates 2025-11-05 14:42:26 +01:00
DJj123dj f7cda48ed6 Improved LICENSE Terminology 2025-11-04 20:07:18 +01:00
DJj123dj 4b485adf82 (v4.1) Additional autoclose/autodelete features 2025-11-04 19:53:40 +01:00
DJj123dj e55438cc67 (v4.1) Added +/-10 Minor Features & Improvements 2025-11-04 19:39:52 +01:00
DJj123dj c7b44232c4 Added transfer command (/transfer) + new stats
With this command you're able to transfer the ticket ownership to another person in the server.
2025-11-02 20:09:38 +01:00
DJj123dj 68a8170570 Minor bugfixes & improvements (priority & topic) 2025-11-02 20:08:57 +01:00
DJj123dj 34ed4edede Added priority commands (/priority get,set) 2025-11-02 18:41:46 +01:00
DJj123dj a40a9b8be8 Added channel topic command (/topic set) 2025-11-02 18:40:49 +01:00
DJj123dj e8f96ef84f Finished channel topic feature 2025-10-28 13:24:08 +01:00
DJj123dj 8413d3c2b7 Started with channel topic auto-update action 2025-10-27 21:19:59 +01:00
DJj123dj 30b6ab4df6 Finished channel topic on ticket creation 2025-10-27 11:41:56 +01:00
DJj123dj 2aef855902 Started working on channel topic again 2025-10-12 22:18:38 +02:00
DJj123dj 4aacdd1607 fix: #122 improved windows OT plugin support
Open Ticket instances with plugins are now also able to exist outside of the default C:\\ drive on windows. This has been fixed by searching for the plugins relative to the bot instead of using an absolute path.
2025-09-21 20:31:47 +02:00
Jasper d66925a450 Added user-nickname channel suffix option 2025-09-14 22:23:42 +02:00
DJj123dj d2cd6be513 Small nickname suffix fixes & discord.js try-catch 2025-09-14 22:22:09 +02:00
DJj123dj 30a0a6c503 (API) Renamed ODPriority --> ODPriorityLevel 2025-09-14 22:09:09 +02:00
DJj123dj 72f84b2566 (API) Class & comment readability improvements 2025-09-14 22:05:30 +02:00
DJj123dj 6ce017b407 Added /topic set command (Part 1, Incomplete) 2025-09-14 22:04:42 +02:00
DJj123dj 415330be21 Added priority system (Part 1, Incomplete) 2025-09-14 22:04:11 +02:00
Steven DUBOIS 21ce2921a1 Add user-nickname option for channel suffixe 2025-09-10 18:38:57 +02:00
DJj123dj b07252151b #181 : Fixed async permission registration issues 2025-08-28 12:46:37 +02:00
DJj123dj 02d37dd177 (API) Implemented opendiscord:reopened in database 2025-08-28 11:25:18 +02:00
DJj123dj 706006e319 Added 9 new statistics in the /stats command
Ticket volume, Average tickets/user, System uptime, Current tickets, Ticket age, Message/Embed/File/Component amount.
2025-08-28 11:20:02 +02:00
DJj123dj 82f2aeaa90 Added autocomplete to panel, ticket & move cmds
Ticket/panel options are now autocompleted instead of being a choice in the slash commands. This also results in the max amount of tickets limit to be increased to infinity.
2025-08-28 09:00:07 +02:00
DJj123dj 793c6f0603 Added: Slow Mode & Updated discord.js 2025-08-28 08:59:16 +02:00
DJj123dj 1ef74f0b6b Custom reason for create-ticket-permissions action
Added support for custom reasons in the opendiscord:create-ticket-permissions action. Now it's easier for plugins to add a custom message when the validation fails.
2025-08-27 14:10:58 +02:00
DJj123dj 337dc3827e Refactor config checker children options
Made the "priority" and "optional" fields in ODCheckerObjectStructure children optional. This improves readability and code simplicity
2025-08-27 11:57:28 +02:00
DJj123dj 810db76e91 Finished adding 10 new config checker validators 2025-08-27 11:46:42 +02:00
DJj123dj f4f6f30fa2 Added: Show global admins in panel message 2025-08-12 14:19:18 +02:00
DJj123dj 8436b19cbf Updated bot versions to v4.1.0 2025-08-05 10:42:35 +02:00
DJj123dj 549c161830 Added all the new config variables for v4.1.0! 2025-08-05 10:42:15 +02:00
DJj123dj adc2433216 Merge branch 'dev' into dev-v4.1.0 2025-06-24 11:05:59 +02:00
DJj123dj 6df3498cb5 Finished autocomplete & context menu interactions
Autocomplete & context menu interactions have been fully implemented in the Open Discord framework. They have not yet been implemented in the Open Ticket bot code though.
2025-06-24 08:43:57 +02:00
DJj123dj 8116d4d8dd Startscreen plugin list rendering improvements 2025-06-21 22:17:31 +02:00
DJj123dj e987c176b7 Add autocomplete manager + context menu bugfixes 2025-06-21 22:16:50 +02:00
DJj123dj 2ea38cd7a5 (Open Discord) Added support for context menu's 2025-06-20 18:20:27 +02:00
DJj123dj b2f2ef3405 Update quickSetup.ts 2025-06-20 15:59:52 +02:00
DJj123dj c2e6f781b9 Finished Quick Setup CLI 2025-06-19 22:43:38 +02:00
DJj123dj 160f89c7cf Improved inline contributor guides
Improved the inline contributor guides for registering new languages, config variables & commands.
2025-06-16 20:11:00 +02:00
DJj123dj 51c4ae0229 Finished Interactive Update CLI Questions
Added all remaining questions including panel configuration, ticket message layout, ticket limits, emoji style & more. Also fixed some smaller bugs.
2025-06-16 20:10:09 +02:00
DJj123dj 2cc2ccc68e Added ODQuickMessage builders 2025-06-16 17:45:28 +02:00
DJj123dj 0a29e13640 Added new questions to quick setup CLI + fixes
Added question 12, 13 & 14 (autoclose, cooldown, close participants policy. Also fixed backFn-bugs and rewrote a few sentences for better spelling. Also added some UI improvements to string-inputs
2025-06-16 15:37:57 +02:00
DJj123dj 31b94718f1 Repositioned + Tested Reaction Role Logs 2025-06-16 14:47:09 +02:00
Jasper 6ac993617e Merge pull #176 from sdehaarte (Reaction Role Logs)
Added Reaction Role Logs
2025-06-16 14:44:08 +02:00
sdehaarte 9f4999b89b Feature Reaction Role Logs. Adds logging functionality for reaction roles. This helps track when users add or remove reaction roles. 2025-06-11 20:06:43 -04:00
DJj123dj dd3c159413 Added new questions to quick setup CLI
Added: channel logs, ticket category, ticket creation count, ticket option configuration (name, descr, button, prefix & suffix)
2025-06-11 21:12:20 +02:00
DJj123dj 72627a7058 Added new config checker utility function
createTemporaryCheckerEnvironment() is able to help you with using `ODCheckerStructure` validators without officially registering them in opendiscord.checkers.
2025-06-11 21:10:29 +02:00
Jasper b683365126 Merge pull request #175 from JasperAtSchool/dev-v4.1.0
(v4.1) Added new string config checker validators (part 1)
2025-06-11 18:51:01 +02:00
JasperAtSchool 03b3495eb2 (v4.1) Added new string config checker validators 2025-06-06 11:14:21 +02:00
DJj123dj af0dc879c6 Merge branch 'dev' into dev-v4.1.0 2025-05-31 11:07:36 +02:00
DJj123dj c243d03216 Merge branch 'dev' into dev-v4.1.0 2025-05-03 21:15:03 +02:00
DJj123dj f117943e00 Added 6th, 7th & 8th questions to quick setup CLI 2025-05-02 17:51:52 +02:00
DJj123dj 951aff093a Added 4th & 5th questions to quick setup 2025-05-01 22:01:04 +02:00
DJj123dj 2acd185d6f Merge branch 'dev' into dev-v4.1.0 2025-05-01 14:26:41 +02:00
DJj123dj 2fbef46a53 Merge branch 'dev' of https://github.com/open-discord-bots/open-ticket into dev 2025-05-01 14:14:39 +02:00
Jasper 8df13b0d36 Open Ticket v4.1.0 Improvements + Minor Additions
Open Ticket v4.1.0 Improvements + Minor Additions
2025-05-01 13:52:45 +02:00
DJj123dj b5dd7158df Update README.md 2025-04-29 17:56:54 +02:00
DJj123dj 05919bb57e Update README.md 2025-04-29 17:51:39 +02:00
DJj123dj 1d6572aa36 Merge branch 'dev' of https://github.com/open-discord-bots/open-ticket into dev 2025-04-29 17:50:51 +02:00
JasperAtSchool 83080a6e4d Removed openticket -> opendiscord migration utils 2025-04-29 09:58:36 +02:00
JasperAtSchool 35fdafb21d Removed deprecated API classes + properties 2025-04-29 09:54:48 +02:00
JasperAtSchool a0e03be103 Added ordinal numbers to config checker. 2025-04-29 09:47:29 +02:00
Jasper d404f343e0 Sync Dev Branches
* Fixed bug due to discord.js new components

* Updated all versions to v4.0.3

* #155 : Fixed reply on creation when disabled.

Fixed a bug that caused the bot to send a partial ephemeral message while "replyOnTicketCreation" was disabled.
2025-04-28 18:30:43 +02:00
Jasper adf4acd443 Merge branch 'dev-v4.1.0' into dev 2025-04-28 18:30:21 +02:00
DJj123dj fd739054fd Added 3th & 4th question to Quick Setup CLI 2025-04-20 12:08:52 +02:00
DJj123dj 2d1724893e Update README.md 2025-04-20 11:14:19 +02:00
DJj123dj 7696202dec Updated Dutch Translations 2025-04-19 21:05:08 +02:00
DJj123dj e36c1992de Added 2nd question in the CLI quick setup 2025-04-17 18:38:54 +02:00
DJj123dj e73c8e9b3b Started with CLI "Quick Setup" feature. 2025-04-16 21:44:23 +02:00
DJj123dj ad623f9650 Finished display names + descriptions (Part 4) 2025-04-16 13:20:07 +02:00
DJj123dj 515b7e1ffc Added more display names + descriptions (Part 3) 2025-04-16 09:18:13 +02:00
DJj123dj 605173e788 Added more display names + descriptions (Part 2) 2025-04-15 22:36:58 +02:00
DJj123dj e129724c12 Added display values for all general.json settings 2025-04-14 14:53:31 +02:00
DJj123dj 49504bc3de Added formatters to all built-in config files.
Added formatters to all built-in JSON config files for the Open Ticket interactive CLI. The new save() feature from the ODJsonConfig class will be using this formatter to save the config to the filesystem.
2025-04-14 14:22:09 +02:00
Jasper f414fd77fc Synced Dev Branches
Sync Dev Branches
2025-04-13 21:11:40 +02:00
DJj123dj 5dcbcbfabc More CLI improvements + layout changes 2025-04-13 21:03:55 +02:00
DJj123dj 6ec228df53 Added 'save()' feature to configs + improvements 2025-04-13 21:03:45 +02:00
DJj123dj 1f3894141a Finished the 'edit config' module from the CLI 2025-04-13 10:33:01 +02:00
DJj123dj 9253377766 CLI improvements 2025-04-12 21:05:52 +02:00
DJj123dj 30352b570a Updated Interactive Setup CLI (Unfinished Part 2) 2025-04-08 13:51:01 +02:00
DJj123dj 1fddad1e96 Improved TS interfaces for the config checker 2025-04-08 13:49:59 +02:00
DJj123dj d545d21bb1 Update cli.ts 2025-04-07 16:47:57 +02:00
DJj123dj 5f79ae909b Added Interactive Setup CLI (Unfinished)
Added the first part of the interactive setup CLI. You can explore the interface but some parts will not work yet. It will also not save to the config file yet.

The "Quick Setup" & "Manage Plugins" parts of the CLI will arrive later this week.
2025-04-06 19:04:09 +02:00
DJj123dj 831e120093 Finished --silent flag 2025-04-05 22:18:45 +02:00
DJj123dj 376fa23288 Started working on --silent flag 2025-04-04 22:40:15 +02:00
132 changed files with 10476 additions and 2302 deletions
+649
View File
@@ -0,0 +1,649 @@
//@ts-check
const fjs = require("formatted-json-stringify")
const fs = require("fs")
const formatter = new fjs.ObjectFormatter(null,true,[
new fjs.ObjectFormatter("_TRANSLATION",true,[
new fjs.PropertyFormatter("otversion"),
new fjs.ArrayFormatter("translators",false,new fjs.PropertyFormatter(null)),
new fjs.PropertyFormatter("lastedited"),
new fjs.PropertyFormatter("language"),
new fjs.PropertyFormatter("automated"),
]),
new fjs.ObjectFormatter("checker",true,[
new fjs.ObjectFormatter("system",true,[
new fjs.PropertyFormatter("typeError"),
new fjs.PropertyFormatter("headerOpenTicket"),
new fjs.PropertyFormatter("typeWarning"),
new fjs.PropertyFormatter("typeInfo"),
new fjs.PropertyFormatter("headerConfigChecker"),
new fjs.PropertyFormatter("headerDescription"),
new fjs.PropertyFormatter("footerError"),
new fjs.PropertyFormatter("footerWarning"),
new fjs.PropertyFormatter("footerSupport"),
new fjs.PropertyFormatter("compactInformation"),
new fjs.PropertyFormatter("dataPath"),
new fjs.PropertyFormatter("dataDocs"),
new fjs.PropertyFormatter("dataMessages"),
]),
new fjs.ObjectFormatter("messages",true,[
new fjs.PropertyFormatter("stringTooShort"),
new fjs.PropertyFormatter("stringTooLong"),
new fjs.PropertyFormatter("stringLengthInvalid"),
new fjs.PropertyFormatter("stringStartsWith"),
new fjs.PropertyFormatter("stringEndsWith"),
new fjs.PropertyFormatter("stringContains"),
new fjs.PropertyFormatter("stringChoices"),
new fjs.PropertyFormatter("stringRegex"),
new fjs.PropertyFormatter("stringInvertedContains"),
new fjs.PropertyFormatter("stringLowercase"),
new fjs.PropertyFormatter("stringUppercase"),
new fjs.PropertyFormatter("stringSpecialCharacters"),
new fjs.PropertyFormatter("stringNoSpaces"),
new fjs.PropertyFormatter("stringCapitalWord"),
new fjs.PropertyFormatter("stringCapitalSentence"),
new fjs.PropertyFormatter("stringPunctuation"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("numberTooShort"),
new fjs.PropertyFormatter("numberTooLong"),
new fjs.PropertyFormatter("numberLengthInvalid"),
new fjs.PropertyFormatter("numberTooSmall"),
new fjs.PropertyFormatter("numberTooLarge"),
new fjs.PropertyFormatter("numberNotEqual"),
new fjs.PropertyFormatter("numberStep"),
new fjs.PropertyFormatter("numberStepOffset"),
new fjs.PropertyFormatter("numberStartsWith"),
new fjs.PropertyFormatter("numberEndsWith"),
new fjs.PropertyFormatter("numberContains"),
new fjs.PropertyFormatter("numberChoices"),
new fjs.PropertyFormatter("numberFloat"),
new fjs.PropertyFormatter("numberNegative"),
new fjs.PropertyFormatter("numberPositive"),
new fjs.PropertyFormatter("numberZero"),
new fjs.PropertyFormatter("numberNan"),
new fjs.PropertyFormatter("numberInvertedContains"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("booleanTrue"),
new fjs.PropertyFormatter("booleanFalse"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("arrayEmptyDisabled"),
new fjs.PropertyFormatter("arrayEmptyRequired"),
new fjs.PropertyFormatter("arrayTooShort"),
new fjs.PropertyFormatter("arrayTooLong"),
new fjs.PropertyFormatter("arrayLengthInvalid"),
new fjs.PropertyFormatter("arrayInvalidTypes"),
new fjs.PropertyFormatter("arrayDouble"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("discordInvalidId"),
new fjs.PropertyFormatter("discordInvalidIdOptions"),
new fjs.PropertyFormatter("discordInvalidToken"),
new fjs.PropertyFormatter("colorInvalid"),
new fjs.PropertyFormatter("emojiTooShort"),
new fjs.PropertyFormatter("emojiTooLong"),
new fjs.PropertyFormatter("emojiCustom"),
new fjs.PropertyFormatter("emojiInvalid"),
new fjs.PropertyFormatter("urlInvalid"),
new fjs.PropertyFormatter("urlInvalidHttp"),
new fjs.PropertyFormatter("urlInvalidProtocol"),
new fjs.PropertyFormatter("urlInvalidHostname"),
new fjs.PropertyFormatter("urlInvalidExtension"),
new fjs.PropertyFormatter("urlInvalidPath"),
new fjs.PropertyFormatter("idNotUnique"),
new fjs.PropertyFormatter("idNonExistent"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("invalidType"),
new fjs.PropertyFormatter("propertyMissing"),
new fjs.PropertyFormatter("propertyOptional"),
new fjs.PropertyFormatter("objectDisabled"),
new fjs.PropertyFormatter("nullInvalid"),
new fjs.PropertyFormatter("switchInvalidType"),
new fjs.PropertyFormatter("objectSwitchInvalid"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("invalidLanguage"),
new fjs.PropertyFormatter("invalidButton"),
new fjs.PropertyFormatter("unusedOption"),
new fjs.PropertyFormatter("unusedQuestion"),
new fjs.PropertyFormatter("dropdownOption"),
new fjs.PropertyFormatter("customInvalidVersion"),
]),
]),
new fjs.ObjectFormatter("actions",true,[
new fjs.ObjectFormatter("buttons",true,[
new fjs.PropertyFormatter("create"),
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("delete"),
new fjs.PropertyFormatter("reopen"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.PropertyFormatter("clear"),
new fjs.PropertyFormatter("helpSwitchSlash"),
new fjs.PropertyFormatter("helpSwitchText"),
new fjs.PropertyFormatter("helpPage"),
new fjs.PropertyFormatter("withReason"),
new fjs.PropertyFormatter("withoutTranscript"),
]),
new fjs.ObjectFormatter("titles",true,[
new fjs.PropertyFormatter("created"),
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("delete"),
new fjs.PropertyFormatter("reopen"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.PropertyFormatter("rename"),
new fjs.PropertyFormatter("move"),
new fjs.PropertyFormatter("add"),
new fjs.PropertyFormatter("remove"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("help"),
new fjs.PropertyFormatter("statsReset"),
new fjs.PropertyFormatter("blacklistAdd"),
new fjs.PropertyFormatter("blacklistRemove"),
new fjs.PropertyFormatter("blacklistGet"),
new fjs.PropertyFormatter("blacklistView"),
new fjs.PropertyFormatter("blacklistAddDm"),
new fjs.PropertyFormatter("blacklistRemoveDm"),
new fjs.PropertyFormatter("clear"),
new fjs.PropertyFormatter("clearTickets"),
new fjs.PropertyFormatter("roles"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("autoclose"),
new fjs.PropertyFormatter("autocloseEnabled"),
new fjs.PropertyFormatter("autocloseDisabled"),
new fjs.PropertyFormatter("autodelete"),
new fjs.PropertyFormatter("autodeleteEnabled"),
new fjs.PropertyFormatter("autodeleteDisabled"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("topicSet"),
new fjs.PropertyFormatter("prioritySet"),
new fjs.PropertyFormatter("priorityGet"),
new fjs.PropertyFormatter("transfer"),
]),
new fjs.ObjectFormatter("descriptions",true,[
new fjs.PropertyFormatter("create"),
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("delete"),
new fjs.PropertyFormatter("reopen"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.PropertyFormatter("rename"),
new fjs.PropertyFormatter("move"),
new fjs.PropertyFormatter("add"),
new fjs.PropertyFormatter("remove"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("helpExplanation"),
new fjs.PropertyFormatter("statsReset"),
new fjs.PropertyFormatter("statsError"),
new fjs.PropertyFormatter("blacklistAdd"),
new fjs.PropertyFormatter("blacklistRemove"),
new fjs.PropertyFormatter("blacklistGetSuccess"),
new fjs.PropertyFormatter("blacklistGetEmpty"),
new fjs.PropertyFormatter("blacklistViewEmpty"),
new fjs.PropertyFormatter("blacklistViewTip"),
new fjs.PropertyFormatter("clearVerify"),
new fjs.PropertyFormatter("clearReady"),
new fjs.PropertyFormatter("rolesEmpty"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("autocloseLeave"),
new fjs.PropertyFormatter("autocloseTimeout"),
new fjs.PropertyFormatter("autodeleteLeave"),
new fjs.PropertyFormatter("autodeleteTimeout"),
new fjs.PropertyFormatter("autocloseEnabled"),
new fjs.PropertyFormatter("autocloseDisabled"),
new fjs.PropertyFormatter("autodeleteEnabled"),
new fjs.PropertyFormatter("autodeleteDisabled"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("ticketMessageLimit"),
new fjs.PropertyFormatter("ticketMessageAutoclose"),
new fjs.PropertyFormatter("ticketMessageAutodelete"),
new fjs.PropertyFormatter("panelReady"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("topicSet"),
new fjs.PropertyFormatter("prioritySet"),
new fjs.PropertyFormatter("priorityGet"),
new fjs.PropertyFormatter("transfer"),
]),
new fjs.ObjectFormatter("modal",true,[
new fjs.PropertyFormatter("closePlaceholder"),
new fjs.PropertyFormatter("deletePlaceholder"),
new fjs.PropertyFormatter("reopenPlaceholder"),
new fjs.PropertyFormatter("claimPlaceholder"),
new fjs.PropertyFormatter("unclaimPlaceholder"),
new fjs.PropertyFormatter("pinPlaceholder"),
new fjs.PropertyFormatter("unpinPlaceholder"),
]),
new fjs.ObjectFormatter("logs",true,[
new fjs.PropertyFormatter("createLog"),
new fjs.PropertyFormatter("closeLog"),
new fjs.PropertyFormatter("closeDm"),
new fjs.PropertyFormatter("deleteLog"),
new fjs.PropertyFormatter("deleteDm"),
new fjs.PropertyFormatter("reopenLog"),
new fjs.PropertyFormatter("reopenDm"),
new fjs.PropertyFormatter("claimLog"),
new fjs.PropertyFormatter("claimDm"),
new fjs.PropertyFormatter("unclaimLog"),
new fjs.PropertyFormatter("unclaimDm"),
new fjs.PropertyFormatter("pinLog"),
new fjs.PropertyFormatter("pinDm"),
new fjs.PropertyFormatter("unpinLog"),
new fjs.PropertyFormatter("unpinDm"),
new fjs.PropertyFormatter("renameLog"),
new fjs.PropertyFormatter("renameDm"),
new fjs.PropertyFormatter("moveLog"),
new fjs.PropertyFormatter("moveDm"),
new fjs.PropertyFormatter("addLog"),
new fjs.PropertyFormatter("addDm"),
new fjs.PropertyFormatter("removeLog"),
new fjs.PropertyFormatter("removeDm"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("blacklistAddLog"),
new fjs.PropertyFormatter("blacklistRemoveLog"),
new fjs.PropertyFormatter("blacklistAddDm"),
new fjs.PropertyFormatter("blacklistRemoveDm"),
new fjs.PropertyFormatter("clearLog"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("transferLog"),
new fjs.PropertyFormatter("transferDm"),
new fjs.PropertyFormatter("prioritySetLog"),
new fjs.PropertyFormatter("prioritySetDm"),
new fjs.PropertyFormatter("roleUpdateLog"),
new fjs.PropertyFormatter("roleUpdateDm"),
]),
]),
new fjs.ObjectFormatter("transcripts",true,[
new fjs.ObjectFormatter("success",true,[
new fjs.PropertyFormatter("visit"),
new fjs.PropertyFormatter("ready"),
new fjs.PropertyFormatter("textFileDescription"),
new fjs.PropertyFormatter("htmlProgress"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("createdChannel"),
new fjs.PropertyFormatter("createdCreator"),
new fjs.PropertyFormatter("createdParticipant"),
new fjs.PropertyFormatter("createdActiveAdmin"),
new fjs.PropertyFormatter("createdEveryAdmin"),
new fjs.PropertyFormatter("createdOther"),
]),
new fjs.ObjectFormatter("errors",true,[
new fjs.PropertyFormatter("retry"),
new fjs.PropertyFormatter("continue"),
new fjs.PropertyFormatter("backup"),
new fjs.PropertyFormatter("error"),
new fjs.PropertyFormatter("title"),
]),
new fjs.ObjectFormatter("text",true,[
new fjs.PropertyFormatter("messagesTitle"),
new fjs.PropertyFormatter("embedTitle"),
new fjs.PropertyFormatter("fileTitle"),
new fjs.PropertyFormatter("fieldsTitle"),
new fjs.PropertyFormatter("reactionsTitle"),
new fjs.PropertyFormatter("statsTitle"),
new fjs.PropertyFormatter("emptyContent"),
new fjs.PropertyFormatter("noTitle"),
new fjs.PropertyFormatter("noDesc"),
]),
]),
new fjs.ObjectFormatter("errors",true,[
new fjs.ObjectFormatter("titles",true,[
new fjs.PropertyFormatter("internalError"),
new fjs.PropertyFormatter("optionMissing"),
new fjs.PropertyFormatter("optionInvalid"),
new fjs.PropertyFormatter("unknownCommand"),
new fjs.PropertyFormatter("noPermissions"),
new fjs.PropertyFormatter("unknownTicket"),
new fjs.PropertyFormatter("deprecatedTicket"),
new fjs.PropertyFormatter("unknownOption"),
new fjs.PropertyFormatter("unknownPanel"),
new fjs.PropertyFormatter("notInGuild"),
new fjs.PropertyFormatter("channelRename"),
new fjs.PropertyFormatter("busy"),
new fjs.PropertyFormatter("permissionError"),
]),
new fjs.ObjectFormatter("descriptions",true,[
new fjs.PropertyFormatter("askForInfo"),
new fjs.PropertyFormatter("askForInfoResolve"),
new fjs.PropertyFormatter("internalError"),
new fjs.PropertyFormatter("optionMissing"),
new fjs.PropertyFormatter("optionInvalid"),
new fjs.PropertyFormatter("optionInvalidChoose"),
new fjs.PropertyFormatter("unknownCommand"),
new fjs.PropertyFormatter("noPermissions"),
new fjs.PropertyFormatter("noPermissionsList"),
new fjs.PropertyFormatter("noPermissionsCooldown"),
new fjs.PropertyFormatter("noPermissionsBlacklist"),
new fjs.PropertyFormatter("noPermissionsLimitGlobal"),
new fjs.PropertyFormatter("noPermissionsLimitGlobalUser"),
new fjs.PropertyFormatter("noPermissionsLimitOption"),
new fjs.PropertyFormatter("noPermissionsLimitOptionUser"),
new fjs.PropertyFormatter("unknownTicket"),
new fjs.PropertyFormatter("deprecatedTicket"),
new fjs.PropertyFormatter("notInGuild"),
new fjs.PropertyFormatter("channelRename"),
new fjs.PropertyFormatter("channelRenameSource"),
new fjs.PropertyFormatter("busy"),
new fjs.PropertyFormatter("closeBeforeMessage"),
new fjs.PropertyFormatter("closeBeforeAdminMessage"),
new fjs.PropertyFormatter("unableToCreateTicket"),
]),
new fjs.ObjectFormatter("optionInvalidReasons",true,[
new fjs.PropertyFormatter("stringRegex"),
new fjs.PropertyFormatter("stringMinLength"),
new fjs.PropertyFormatter("stringMaxLength"),
new fjs.PropertyFormatter("numberInvalid"),
new fjs.PropertyFormatter("numberMin"),
new fjs.PropertyFormatter("numberMax"),
new fjs.PropertyFormatter("numberDecimal"),
new fjs.PropertyFormatter("numberNegative"),
new fjs.PropertyFormatter("numberPositive"),
new fjs.PropertyFormatter("numberZero"),
new fjs.PropertyFormatter("channelNotFound"),
new fjs.PropertyFormatter("userNotFound"),
new fjs.PropertyFormatter("roleNotFound"),
new fjs.PropertyFormatter("memberNotFound"),
new fjs.PropertyFormatter("mentionableNotFound"),
new fjs.PropertyFormatter("channelType"),
new fjs.PropertyFormatter("notInGuild"),
]),
new fjs.ObjectFormatter("permissions",true,[
new fjs.PropertyFormatter("developer"),
new fjs.PropertyFormatter("owner"),
new fjs.PropertyFormatter("admin"),
new fjs.PropertyFormatter("moderator"),
new fjs.PropertyFormatter("support"),
new fjs.PropertyFormatter("member"),
new fjs.PropertyFormatter("discord-administrator"),
]),
new fjs.ObjectFormatter("actionInvalid",true,[
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("reopen"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.PropertyFormatter("add"),
new fjs.PropertyFormatter("remove"),
]),
]),
new fjs.ObjectFormatter("params",true,[
new fjs.ObjectFormatter("uppercase",true,[
new fjs.PropertyFormatter("ticket"),
new fjs.PropertyFormatter("tickets"),
new fjs.PropertyFormatter("reason"),
new fjs.PropertyFormatter("creator"),
new fjs.PropertyFormatter("remaining"),
new fjs.PropertyFormatter("added"),
new fjs.PropertyFormatter("removed"),
new fjs.PropertyFormatter("filter"),
new fjs.PropertyFormatter("method"),
new fjs.PropertyFormatter("type"),
new fjs.PropertyFormatter("blacklisted"),
new fjs.PropertyFormatter("panel"),
new fjs.PropertyFormatter("command"),
new fjs.PropertyFormatter("system"),
new fjs.PropertyFormatter("true"),
new fjs.PropertyFormatter("false"),
new fjs.PropertyFormatter("syntax"),
new fjs.PropertyFormatter("originalName"),
new fjs.PropertyFormatter("newName"),
new fjs.PropertyFormatter("until"),
new fjs.PropertyFormatter("validOptions"),
new fjs.PropertyFormatter("validPanels"),
new fjs.PropertyFormatter("autoclose"),
new fjs.PropertyFormatter("autodelete"),
new fjs.PropertyFormatter("startupDate"),
new fjs.PropertyFormatter("version"),
new fjs.PropertyFormatter("name"),
new fjs.PropertyFormatter("role"),
new fjs.PropertyFormatter("status"),
new fjs.PropertyFormatter("claimed"),
new fjs.PropertyFormatter("pinned"),
new fjs.PropertyFormatter("creationDate"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("noone"),
new fjs.PropertyFormatter("open"),
new fjs.PropertyFormatter("closed"),
new fjs.PropertyFormatter("priority"),
new fjs.PropertyFormatter("participants"),
new fjs.PropertyFormatter("yes"),
new fjs.PropertyFormatter("no"),
new fjs.PropertyFormatter("option"),
new fjs.PropertyFormatter("topic"),
new fjs.PropertyFormatter("uptime"),
new fjs.PropertyFormatter("messages"),
new fjs.PropertyFormatter("embeds"),
new fjs.PropertyFormatter("files"),
new fjs.PropertyFormatter("components"),
new fjs.PropertyFormatter("cooldown"),
new fjs.PropertyFormatter("maxTickets"),
new fjs.PropertyFormatter("admins"),
new fjs.PropertyFormatter("roles"),
new fjs.PropertyFormatter("size"),
]),
new fjs.ObjectFormatter("lowercase",true,[
new fjs.PropertyFormatter("text"),
new fjs.PropertyFormatter("html"),
new fjs.PropertyFormatter("command"),
new fjs.PropertyFormatter("modal"),
new fjs.PropertyFormatter("button"),
new fjs.PropertyFormatter("dropdown"),
new fjs.PropertyFormatter("method"),
]),
]),
new fjs.ObjectFormatter("commands",true,[
new fjs.PropertyFormatter("reason"),
new fjs.PropertyFormatter("help"),
new fjs.PropertyFormatter("panel"),
new fjs.PropertyFormatter("panelId"),
new fjs.PropertyFormatter("panelAutoUpdate"),
new fjs.PropertyFormatter("ticket"),
new fjs.PropertyFormatter("ticketId"),
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("delete"),
new fjs.PropertyFormatter("deleteNoTranscript"),
new fjs.PropertyFormatter("reopen"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("claimUser"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("move"),
new fjs.PropertyFormatter("moveId"),
new fjs.PropertyFormatter("rename"),
new fjs.PropertyFormatter("renameName"),
new fjs.PropertyFormatter("add"),
new fjs.PropertyFormatter("addUser"),
new fjs.PropertyFormatter("remove"),
new fjs.PropertyFormatter("removeUser"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("blacklist"),
new fjs.PropertyFormatter("blacklistView"),
new fjs.PropertyFormatter("blacklistAdd"),
new fjs.PropertyFormatter("blacklistRemove"),
new fjs.PropertyFormatter("blacklistGet"),
new fjs.PropertyFormatter("blacklistGetUser"),
new fjs.PropertyFormatter("stats"),
new fjs.PropertyFormatter("statsReset"),
new fjs.PropertyFormatter("statsGlobal"),
new fjs.PropertyFormatter("statsUser"),
new fjs.PropertyFormatter("statsUserUser"),
new fjs.PropertyFormatter("statsTicket"),
new fjs.PropertyFormatter("statsTicketTicket"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("clear"),
new fjs.PropertyFormatter("clearFilter"),
new fjs.ObjectFormatter("clearFilters",true,[
new fjs.PropertyFormatter("all"),
new fjs.PropertyFormatter("open"),
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.PropertyFormatter("autoclose"),
]),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("autoclose"),
new fjs.PropertyFormatter("autocloseDisable"),
new fjs.PropertyFormatter("autocloseEnable"),
new fjs.PropertyFormatter("autocloseEnableTime"),
new fjs.PropertyFormatter("autodelete"),
new fjs.PropertyFormatter("autodeleteDisable"),
new fjs.PropertyFormatter("autodeleteEnable"),
new fjs.PropertyFormatter("autodeleteEnableTime"),
new fjs.TextFormatter(""),
new fjs.PropertyFormatter("topic"),
new fjs.PropertyFormatter("topicSet"),
new fjs.PropertyFormatter("topicValue"),
new fjs.PropertyFormatter("topicList"),
new fjs.PropertyFormatter("priority"),
new fjs.PropertyFormatter("prioritySet"),
new fjs.PropertyFormatter("priorityValue"),
new fjs.PropertyFormatter("priorityGet"),
new fjs.PropertyFormatter("priorityList"),
new fjs.PropertyFormatter("transfer"),
new fjs.PropertyFormatter("transferUser"),
]),
new fjs.ObjectFormatter("helpMenu",true,[
new fjs.PropertyFormatter("help"),
new fjs.PropertyFormatter("ticket"),
new fjs.PropertyFormatter("close"),
new fjs.PropertyFormatter("delete"),
new fjs.PropertyFormatter("reopen"),
new fjs.PropertyFormatter("pin"),
new fjs.PropertyFormatter("unpin"),
new fjs.PropertyFormatter("move"),
new fjs.PropertyFormatter("rename"),
new fjs.PropertyFormatter("claim"),
new fjs.PropertyFormatter("unclaim"),
new fjs.PropertyFormatter("add"),
new fjs.PropertyFormatter("remove"),
new fjs.PropertyFormatter("panel"),
new fjs.PropertyFormatter("blacklistView"),
new fjs.PropertyFormatter("blacklistAdd"),
new fjs.PropertyFormatter("blacklistRemove"),
new fjs.PropertyFormatter("blacklistGet"),
new fjs.PropertyFormatter("statsGlobal"),
new fjs.PropertyFormatter("statsTicket"),
new fjs.PropertyFormatter("statsUser"),
new fjs.PropertyFormatter("statsReset"),
new fjs.PropertyFormatter("autocloseDisable"),
new fjs.PropertyFormatter("autocloseEnable"),
new fjs.PropertyFormatter("autodeleteDisable"),
new fjs.PropertyFormatter("autodeleteEnable"),
new fjs.ObjectFormatter("categories",true,[
new fjs.PropertyFormatter("general"),
new fjs.PropertyFormatter("basicTicket"),
new fjs.PropertyFormatter("advancedTicket"),
new fjs.PropertyFormatter("userTicket"),
new fjs.PropertyFormatter("admin"),
new fjs.PropertyFormatter("advanced"),
new fjs.PropertyFormatter("extra"),
])
]),
new fjs.ObjectFormatter("stats",true,[
new fjs.ObjectFormatter("scopes",true,[
new fjs.PropertyFormatter("global"),
new fjs.PropertyFormatter("system"),
new fjs.PropertyFormatter("user"),
new fjs.PropertyFormatter("ticket"),
new fjs.PropertyFormatter("participants"),
new fjs.PropertyFormatter("messages"),
]),
new fjs.ObjectFormatter("properties",true,[
new fjs.PropertyFormatter("ticketsCreated"),
new fjs.PropertyFormatter("ticketsClosed"),
new fjs.PropertyFormatter("ticketsDeleted"),
new fjs.PropertyFormatter("ticketsReopened"),
new fjs.PropertyFormatter("ticketsAutoclosed"),
new fjs.PropertyFormatter("ticketsClaimed"),
new fjs.PropertyFormatter("ticketsPinned"),
new fjs.PropertyFormatter("ticketsMoved"),
new fjs.PropertyFormatter("usersBlacklisted"),
new fjs.PropertyFormatter("transcriptsCreated"),
new fjs.PropertyFormatter("ticketsAutodeleted"),
new fjs.PropertyFormatter("ticketsTransferred"),
new fjs.PropertyFormatter("ticketVolume"),
new fjs.PropertyFormatter("averageTickets"),
new fjs.PropertyFormatter("currentTickets"),
new fjs.PropertyFormatter("age"),
new fjs.PropertyFormatter("responseTime"),
new fjs.PropertyFormatter("resolutionTime"),
new fjs.PropertyFormatter("createdOn"),
new fjs.PropertyFormatter("createdBy"),
new fjs.PropertyFormatter("closedOn"),
new fjs.PropertyFormatter("closedBy"),
new fjs.PropertyFormatter("claimedOn"),
new fjs.PropertyFormatter("claimedBy"),
new fjs.PropertyFormatter("pinnedOn"),
new fjs.PropertyFormatter("pinnedBy"),
new fjs.PropertyFormatter("deletedOn"),
new fjs.PropertyFormatter("deletedBy"),
]),
new fjs.ObjectFormatter("roles",true,[
new fjs.PropertyFormatter("developer"),
new fjs.PropertyFormatter("serverOwner"),
new fjs.PropertyFormatter("serverAdmin"),
new fjs.PropertyFormatter("moderator"),
new fjs.PropertyFormatter("support"),
new fjs.PropertyFormatter("member"),
])
]),
new fjs.ObjectFormatter("panel",true,[
new fjs.PropertyFormatter("selectTicket"),
new fjs.PropertyFormatter("selectRole"),
new fjs.PropertyFormatter("selectOption"),
]),
new fjs.ObjectFormatter("priorities",true,[
new fjs.PropertyFormatter("urgent"),
new fjs.PropertyFormatter("veryHigh"),
new fjs.PropertyFormatter("high"),
new fjs.PropertyFormatter("normal"),
new fjs.PropertyFormatter("low"),
new fjs.PropertyFormatter("veryLow"),
new fjs.PropertyFormatter("none"),
]),
])
for (const language of fs.readdirSync(".docs/languages/")){
if (!fs.existsSync("./languages/"+language)){
console.log("language:",language,"does not exist yet in the primary ./languages/ folder. Unable to merge!")
continue
}
console.log("merging "+language+"...")
const original = JSON.parse(fs.readFileSync("./languages/"+language).toString())
const newSentences = JSON.parse(fs.readFileSync(".docs/languages/"+language).toString())
for (const key of Object.keys(newSentences)){
if (key.startsWith("_")) continue
try{
const splitted = key.split(".")
let currentObject = original
splitted.forEach((property,index) => {
let shouldBeObject = (splitted.length-1 !== index)
if (shouldBeObject && typeof currentObject[property] == "object"){
currentObject = currentObject[property]
}else if (shouldBeObject && typeof currentObject[property] == "undefined"){
currentObject[property] = {}
currentObject = currentObject[property]
}else if (typeof currentObject[property] == "string" || typeof currentObject[property] == "undefined"){
currentObject[property] = newSentences[key]
}else{
console.log("Failed to merge key:",key,"in file:",language,"--> Invalid type:",typeof currentObject[property])
}
})
}catch(err){
console.log("Failed to merge key:",key,"in file:",language)
}
}
original["_TRANSLATION"]["lastedited"] = new Date().toLocaleDateString("nl-BE",{day:"2-digit",month:"2-digit",year:"numeric"})
original["_TRANSLATION"]["otversion"] = "v4.1.0"
const finalText = formatter.stringify(original)
fs.writeFileSync("./languages/"+language,finalText)
}
+6 -3
View File
@@ -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.0.7-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.0.7)
[![version](https://img.shields.io/badge/version-4.1.0-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.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.1.0.json`**](openticket-egg-v4.1.0.json)
- This egg will always use Open Ticket `v4.1.0`. Open Ticket updates will not have an effect on this egg.
[**`openticket-egg-v4.0.7.json`**](openticket-egg-v4.0.7.json)
- This egg will always use Open Ticket `v4.0.7`. Open Ticket updates will not have an effect on this egg.
@@ -54,6 +57,6 @@ It's recommended to provide at least `1GB` of **Memory/RAM** and `5GB` of **disk
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="170px">
**Pterodactyl Eggs**<br>
[changelog](https://otgithub.dj-dj.be/releases) - [documentation](https://otdocs.dj-dj.be) - [tutorial](https://www.youtube.com/watch?v=2jK9kAf6ASU) - [website](https://openticket.dj-dj.be) - [discord](https://discord.dj-dj.be)<br>
[Changelog](https://otgithub.dj-dj.be/releases) - [Documentation](https://otdocs.dj-dj.be) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms#terms) - [Privacy Policy](https://www.dj-dj.be/terms#privacy)
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms) - [Privacy Policy](https://www.dj-dj.be/privacy) - [Support Us](https://github.com/sponsors/DJj123dj)
+62
View File
@@ -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.1.0)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v4.1.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.1.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.1.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"
}
]
}
+3 -3
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.0.7-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.0.7) [![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.1.0-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.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>
@@ -87,6 +87,6 @@ you are **REQUIRED to send the bug privately via one of the following methods:**
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="170px">
**Contributing Guidelines**<br>
[changelog](https://otgithub.dj-dj.be/releases) - [documentation](https://otdocs.dj-dj.be) - [tutorial](https://www.youtube.com/watch?v=2jK9kAf6ASU) - [website](https://openticket.dj-dj.be) - [discord](https://discord.dj-dj.be)<br>
[Changelog](https://otgithub.dj-dj.be/releases) - [Documentation](https://otdocs.dj-dj.be) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms#terms) - [Privacy Policy](https://www.dj-dj.be/terms#privacy)
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms) - [Privacy Policy](https://www.dj-dj.be/privacy) - [Support Us](https://github.com/sponsors/DJj123dj)
+11 -12
View File
@@ -18,16 +18,15 @@ This list will be updated on every release.
- ❌ Fully Deprecated
| Version | Supported | Notes |
|------------|-----------|------------------------------------------|
| 4.1.0 | 🟦 | |
| 4.0.7 | ✅ | |
| 4.0.6 | ✅ | |
| 4.0.5 | ✅ | |
| 4.0.4 | ✅ | Supported until July 2025 (LTS) |
| 4.0.3 | 🚧 | Transcripts v2.0 (Offline August 2025) |
| 4.0.2 | 🟧 | Documentation Only (discord.js bug) |
| 4.0.1 | 🟧 | Documentation Only |
| 4.0.0 | 🟧 | Documentation Only |
|------------|-----------|---------------------------------------------------------------|
| 4.2.0 | 🟦 | Transcripts v2.0 will be taken offline when v4.2 is released. |
| 4.1.1 | 🟦 | |
| 4.1.0 | ✅ | |
| 4.0.7 | ✅ | Supported Until October 2025 (LTS) |
| 4.0.6 | 🚧 | |
| 4.0.5 | 🚧 | |
| 4.0.4 | 🚧 | |
| < 4.0.4 | 🟧 | Deprecated Transcripts v2.0, Documentation Only |
| < 4.0.0 | ❌ | |
### 🕷️ Reporting Vulnerabilities
@@ -48,6 +47,6 @@ If possible, try to provide screenshots!
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="170px">
**Security Policy**<br>
[changelog](https://otgithub.dj-dj.be/releases) - [documentation](https://otdocs.dj-dj.be) - [tutorial](https://www.youtube.com/watch?v=2jK9kAf6ASU) - [website](https://openticket.dj-dj.be) - [discord](https://discord.dj-dj.be)<br>
[Changelog](https://otgithub.dj-dj.be/releases) - [Documentation](https://otdocs.dj-dj.be) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms#terms) - [Privacy Policy](https://www.dj-dj.be/terms#privacy)
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms) - [Privacy Policy](https://www.dj-dj.be/privacy) - [Support Us](https://github.com/sponsors/DJj123dj)
+1
View File
@@ -19,4 +19,5 @@ otdebug.txt
.docs/*
!.docs/createDocs.js
!.docs/mergeTranslations.js
!.docs/typedoc-config.json
+25 -2
View File
@@ -1,6 +1,12 @@
<!-- omit from toc -->
# Open Ticket (License)
© 2025 - DJj123dj & Contributors
#### Table Of Contents
- [General License](#general-license)
- [Additional Terms for Open Ticket](#additional-terms-for-open-ticket)
### General License
```
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
@@ -679,11 +685,28 @@ Public License instead of this License. But first, please read
```
### Additional Terms for Open Ticket
#### Terminology:
1. **“Software”**
Refers to the Open Ticket source code, documentation, and any associated files distributed under this license, including any modifications or derivative works thereof.
1. **“Public Discord Bot**
A Discord bot that is made publicly available for use by multiple, unrelated Discord servers or users, where the bot’s functionality is provided as a service rather than for private or personal use.
1. **“Monetized Service”**
Refers to any service or deployment that generates direct or indirect revenue through the use of the Software. This includes, but is not limited to, subscriptions, premium features, paid hosting, advertisements.
1. **“For-Profit Discord Server”**
Means a Discord server that generates income, revenue, or other material benefits, whether through memberships, donations, or other monetization strategies.
1. **“Private Use”**
Refers to using the Software on a limited number of Discord servers controlled or operated by a single individual or organization, not made available as a public service to multiple unrelated users.
#### Additional Terms:
The following additional terms apply to the use of this software:
1. This software may not be used as part of a public Discord bot service that is monetized (e.g., generating revenue through subscriptions, donations, or other forms of payment) without the prior written permission of the original author.
1. This software may not be used as (or part of) a public Discord bot that is monetized (e.g., generating revenue through subscriptions, donations, or other forms of payment) without the prior written permission of the original author.
2. This software may not be used as part of a public Discord bot service on multiple Discord servers without attribution to the original author.
2. This software may not be used as (or part of) a public Discord bot on multiple Discord servers without attribution to the original author.
3. You are allowed to use this bot in a Discord server that makes money or is for-profit, provided that the income is not generated directly from the ticket bot or its functionality.
+98 -91
View File
@@ -1,5 +1,5 @@
> ### 🎥 Content Creators 🎥
> Hey there! We're searching for content creators that would want to create a tutorial or setup guide for Open Ticket!<br>
> ### 🎥 Content Creators
> Hey there! We're searching for content creators that would like to create a tutorial or setup guide for Open Ticket!<br>
> [📌 More Information](.github/CONTENT_CREATORS.md)
---
@@ -8,7 +8,7 @@
<br>Powered By<br>
<img src="https://apis.dj-dj.be/cdn/opendiscord/logo.png" alt="Open Ticket" width="170px"><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.0.7"><img alt="Open Ticket Version" src="https://img.shields.io/badge/version-4.0.7-brightgreen.svg?style=flat-square"></img></a>
<a href="https://github.com/open-discord-bots/open-ticket/releases/tag/v4.1.0"><img alt="Open Ticket Version" src="https://img.shields.io/badge/version-4.1.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>
@@ -19,48 +19,50 @@
</p>
<p align="center">
Open Ticket is the most advanced & customisable discord ticket bot that you will ever find! You can customise up to 300+ variables! This includes <code>Html Transcripts</code>, <code>Advanced Plugins</code>, <code>Custom Embeds</code>, <code>Questions/Modals</code>, <code>Stats</code> & more!
You're also able to customise every little aspect of the bot! From embeds to transcripts. Open Ticket is also translated in more than <code>36 Languages</code>! If you need any help, feel free to join our <a href="https://discord.dj-dj.be">discord server</a>!
Open Ticket is the most advanced & customisable discord ticket bot available! You are able to customise up to 300+ settings and aspects! This includes <code>Html Transcripts</code>, <code>Advanced Plugins</code>, <code>Custom Embeds</code>, <code>Questions/Modals</code>, <code>Stats</code> & more!
The bot is translated in more than <code>36 Languages</code> and has been battle tested in large Discord servers! If you need any help, feel free to join our <a href="https://discord.dj-dj.be">discord server</a>!
</p>
<p align="center"><b>⭐️ Help us grow by giving a star! ⭐️</b></p>
<h3 align="center"><b>⭐️ Help us grow by giving a star! ⭐️</b></h3>
### 📌 Features
- **🦇 pterodactyl support** - Open Ticket works perfect on Pterodactyl based panels! [(Download official eggs)](.eggs/README.md)
- **💩 no bloat** - Your Open Ticket bot doesn't contain any form of bloat or credits!
- **🔒 tested & secure** - Open Ticket has been battletested by thousands of servers and is very secure!
- **📈 scalable** - Open Ticket is made to handle huge servers! (Already tested in **servers with 100k members**)
- **📄 HTML transcripts** - Make use of the most customisable, beautiful and easy-to-use HTML Transcripts!
- **✅ ticket actions** - Close, Reopen, Delete, Claim, Pin, Rename & Move all your tickets!
- **🇬🇧 translation** - Open Ticket has been translated in more than **36 languages** by our community!
- **🎨 customisation** - Open Ticket has been created around customisation, everything can be customised!
- **🖥️ interactions** - The bot has full support for Buttons, Dropdowns, Slash Commands and Modals!
- **∞ unlimited** - Create an infinite amount of tickets & panels!
- **📝 advanced plugins** - Create advanced plugins or use pre-made ones by our community!
- **👥 user management** - Add & Remove users from all tickets!
- **📊 detailed stats** - Open Ticket has ticket, user & global staticstics available for everyone!
- **🚫 blacklist** - Blacklist users to prevent them from creating a ticket!
- **❓ questions** - Let users answer questions in a modal before the ticket is created!
- **📦 slash & text** - Open Ticket supports both slash & text commands!
- **📥 extra buttons** - The bot also supports Reaction Roles & Url Buttons, because why not ¯\\_(ツ)_/¯
- **✨ commands** - The bot contains more than 25 commands!
- **⏳ Quick Setup** - Using the interactive Quick Setup CLI, you can **configure Open Ticket in less than 5min!**
- **🦇 Pterodactyl Support** - Open Ticket works perfect on Pterodactyl based panels. [(Download official eggs)](.eggs/README.md)
- **💩 No Credits** - Your bot won't contain any form of bloat or credits. It's all yours!
- **🔒 Private & Secure** - It has been battletested by thousands of servers and **respects security & privacy.**
- **📈 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.
- **🎨 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.
- **📝 Advanced Plugins** - Create advanced plugins or use [**pre-made plugins**](#-plugins) by our community.
- **👥 Participants** - Add or remove participants & transfer ownership from one user to another.
- **📊 Detailed Statistics** - With more than **50+ statistics** for tickets, users & the server.
- **🚫 Blacklist** - Blacklist users to prevent them from creating new tickets.
- **🚨 Priorities** - Assign different **priority levels** to tickets to mark them as important.
- **❓ Modal Questions** - Give users the ability to **answer questions** in a modal before their ticket is created.
- **✨ Commands** - Manage all your tickets with more than 28+ commands.
- **🤖 Automation** - Automate ticket handling with **autoclose, autodelete** & slow mode.
- **😎 Additional Features** - For some weird reason, the bot also supports Reaction Role & URL Buttons.
#### And more using plugins!
- **💬 reviews** - Create & customise your own review system!
- **📢 feedback** - Collect feedback & create forms for people to answer!
- **🏷️ tags** - Create tags & answer questions automatically using keywords!
- **📝 forms** - Create advanced forms and ask people for additional details!
- **🔄 rotating status** - Create a rotating bot status & use dynamic variables from the bot!
- **💾 sqlite database** - Use an `sqlite` database for increased performances!
- **🎉 custom embeds** - Create your own embeds and use them in your server!
- **⏰ reminders** - Create & manage customisable reminders in your server!
- **🎨 customisation** - Yep, you heard it right! Even more customisation!
#### And even more using [pre-made community plugins](#-plugins)!
- **💬 Reviews** - Create & manage a support review system.
- **📢 Feedback** - Collect feedback & create forms for users to answer.
- **⏰ Reminders** - Create & manage customisable reminders.
- **🏷️ Tags** - Create tags & answer questions automatically using keywords.
- **📝 Forms** - Create advanced forms and automatically ask for repetitive questions.
- **🔄 Channel Display** - Create a voice channel with realtime statistics from the ticket system.
- **💾 SQLite Database** - Use an `SQLite` database for increased performance.
- **🎉 Custom Embeds** - Create your own embeds and send them using a command.
- **🎨 Customisation** - Yep, you heard it right. Even more customisation!
- **😁 And so much more...**
> ### 📦 Resources
> Not all resources are accurate yet! We are working on this.<br>
> <a href="https://www.youtube.com/watch?v=2jK9kAf6ASU"><img src="https://apis.dj-dj.be/cdn/openticket/open-ticket-tutorial.png" alt="Open Ticket Tutorial" width="180px"></a>
> <a href="https://openticket.dj-dj.be/message.html?msg=The%20current%20tutorial%20is%20outdated,%20we%27re%20still%20working%20on%20a%20new%20one!"><img src="https://apis.dj-dj.be/cdn/openticket/open-ticket-docs.png" alt="Open Ticket Docs" width="180px"></a>
> These resources will help with configuration and usage of the bot:<br>
> <!--<a href="https://openticket.dj-dj.be/message.html?msg=The%20current%20tutorial%20is%20outdated,%20we%27re%20still%20working%20on%20a%20new%20one!"><img src="https://apis.dj-dj.be/cdn/openticket/open-ticket-tutorial.png" alt="Open Ticket Tutorial" width="180px"></a>-->
> <a href="https://otdocs.dj-dj.be"><img src="https://apis.dj-dj.be/cdn/openticket/open-ticket-docs.png" alt="Open Ticket Docs" width="180px"></a>
> <a href="https://github.com/open-discord-bots/plugins"><img src="https://apis.dj-dj.be/cdn/openticket/open-ticket-plugins.png" alt="Open Ticket Plugins" width="180px"></a>
### ❤️ Sponsors
@@ -83,19 +85,19 @@ A big thanks to all our sponsors! Without them, it wouldn't be possible to creat
<img src="https://github.com/BENZORICH.png" alt="BENZORICH" width="40px">
</a>
### ⏱️ Quick Setup
### ⏱️ Quick Setup (Using CLI)
> 1. Download the latest version of Open Ticket on [Github](https://github.com/open-discord-bots/open-ticket).
> 2. Make sure node.js & npm are installed using `node -v` (minimum `v18`).
> 2. Make sure node.js & npm are installed using `node -v` (minimum `v20`).
> 3. Install any required dependencies using `npm install`.
> 4. Configure the bot in the `./config/` directory.
> 5. Start the bot using `npm start` or `node index.js`
> 4. Start the **Quick Setup CLI** using `npm run setup`.
> 5. Click on `> ⏱️ Quick Setup` and follow the instructions.
> 6. Start the bot using `npm start` or `node index.js`
> - The bot will let you know any existing config errors.
> - Fix these errors and restart the bot.
> 6. Enjoy using Open Ticket!
> 7. Enjoy using Open Ticket!
> ### [📔 Visit Documentation](https://otdocs.dj-dj.be)
>
> ### 🖥️ Recommended Hostings
> - [⭐ **Peakhosting.nl**](https://peakhosting.nl/) - Official support for Open Ticket pterodactyl eggs.
> - **Any Pterodactyl-Based Panel** - Easy installation & configuration.
> - **A Virtual Private Server (VPS)** - Extra customisation & more stability. Recommended for large servers.
>
@@ -115,64 +117,69 @@ A list of people that contributed or provided the most support for Open Ticket.
<td align="center"><img src="https://github.com/smetsliam.png" alt="Profile Picture" width="80px"></td>
<td align="center"><img src="https://github.com/FrankVissers.png" alt="Profile Picture" width="80px"></td>
<td align="center"><img src="https://github.com/Sank34.png" alt="Profile Picture" width="80px"></td>
<td align="center"><img src="https://github.com/Roppl3r.png" alt="Profile Picture" width="80px"></td>
<td align="center"><img src="https://github.com/guillee3.png" alt="Profile Picture" width="80px"></td>
</tr>
<tr>
<th><a href="https://github.com/DJj123dj">💻 DJj123dj</a></th>
<th><a href="https://github.com/smetsliam">💬 smetsliam</a></th>
<th><a href="https://github.com/FrankVissers">💬 Frank Vissers</a></th>
<th><a href="https://github.com/Sank34">💬 Sanke</a></th>
<th><a href="https://github.com/Roppl3r">🧩 Roppl3r</a></th>
<th><a href="https://github.com/guillee3">🧩 Guillee3</a></th>
</tr>
</table>
### 💬 Translators
With the amazing support of our translators, we've been able to translate Open Ticket in more than **36 languages**!
As a result, you're able to enjoy using Open Ticket in your own native language.
- **Categories: 🟢 Available - 🤖 Made Using AI - 🔴 Unavailable/Outdated**
#### Categories:
- **🟢 Available**
- **⏳ In Progress (Incomplete)**
- **🤖 Made Using AI**
- **🟠 Incomplete**
- **🔴 Unavailable/Outdated**
|🔍 |Languages (36) |Maintainer (Github/Discord) |
|---|---------------------|--------------------------------|
|----|---------------------|--------------------------------|
|🟢 |🇬🇧 English |djj123dj |
|🟢 |🇳🇱 Dutch |djj123dj |
|🟢 |🇩🇪 German |benzorich |
|🟢 |🇪🇸 Spanish |redactado & josuens |
|🟢 |🇵🇹 Portuguese |quiradon |
|🟢 |❓ Catalan |guillee3 |
|🟢 |🇨🇿 Czech |spyeye_ |
|🟢 |🇭🇺 Hungarian |kornel0706 |
|🟢 |🇷🇴 Romanian |sankedev |
|🟢 |🇺🇦 Ukrainian |anderskiy |
|🟢 |🇮🇩 Indonesian |erxg |
|🟢 |🇮🇹 Italian |fraden1mvp. |
|🟢 |🇩🇰 Danish |the_gamer |
|🟢 |🇹🇭 Thai |modshd |
|🟢 |🇹🇷 Turkish |palestinian |
|🟢 |🇫🇷 French |guillee.3 |
|🟢 |🇦🇪 Arabic |palestinian |
|🟢 |🇮🇳 Hindi |an_developer |
|🟢 |🇱🇹 Lithuanian |tsgindrius |
|🟢 |🇵🇱 Polish |danoglez |
|🟢 |🇳🇴 Norwegian |NoOneNook |
|🟢 |🇸🇪 Swedish |NoOneNook |
|🟢 |🇮🇷 Persian |dysashop & zhavis |
|🟢 |🇧🇩 Bengali |HanumeshGupta |
|🟢 |🇮🇳 Hindi |challenger_nova |
|🟢⏳ |🇩🇪 German |benzorich |
|🟢⏳ |🇪🇸 Spanish |redactado & josuens |
|🟢⏳ |🇫🇷 French |guillee.3 |
|🟢⏳ |🇵🇹 Portuguese |quiradon |
|🟢⏳ |🇨🇿 Czech |spyeye_ |
|🟢⏳ |🇭🇺 Hungarian |kornel0706 |
|🟢⏳ |🇷🇴 Romanian |sankedev |
|🟢⏳ |🇺🇦 Ukrainian |anderskiy |
|🟢⏳ |🇮🇹 Italian |fraden1mvp. |
|🟢⏳ |🇩🇰 Danish |the_gamer |
|🟢⏳ |🇹🇭 Thai |modshd |
|🟢⏳ |🇹🇷 Turkish |palestinian |
|🟢⏳ |🇦🇪 Arabic |palestinian |
|🟢⏳ |🇱🇹 Lithuanian |tsgindrius |
|🟢⏳ |🇵🇱 Polish |danoglez |
|🟢⏳ |🇳🇴 Norwegian |NoOneNook |
|🟢⏳ |🇸🇪 Swedish |NoOneNook |
|🟢⏳ |🇮🇷 Persian |dysashop & zhavis |
|🟢⏳ |🇧🇩 Bengali |HanumeshGupta |
|🤖 |🇪🇪 Estonian |iamnotmega |
|🤖 |🇫🇮 Finnish |iamnotmega |
|🤖 |🇷🇺 Russian |NoOneNook |
|🤖 |🇱🇻 Latvian |NoOneNook |
|🤖 |🇻🇳 Vietnamese |ngocdiep2006 |
|🤖 |🇯🇵 Japanese |HanumeshGupta |
|🤖 |🇬🇷 Greek |HanumeshGupta |
|🤖 |🇸🇮 Slovenian |HanumeshGupta |
|🤖 |🇰🇷 Korean |HanumeshGupta |
|🤖 |🇮🇳 Tamil |HanumeshGupta |
|🤖 |🇨🇳 Simplified Chinese |HanumeshGupta |
|🤖 |❓ Kurdish |HanumeshGupta |
|🤖⏳ |🇷🇺 Russian |NoOneNook |
|🤖⏳ |🇱🇻 Latvian |NoOneNook |
|🤖⏳ |🇻🇳 Vietnamese |ngocdiep2006 |
|🤖⏳ |🇯🇵 Japanese |HanumeshGupta |
|🤖⏳ |🇬🇷 Greek |HanumeshGupta |
|🤖⏳ |🇸🇮 Slovenian |HanumeshGupta |
|🤖⏳ |🇰🇷 Korean |HanumeshGupta |
|🤖⏳ |🇮🇳 Tamil |HanumeshGupta |
|🤖⏳ |🇨🇳 Simplified Chinese |HanumeshGupta |
|🤖⏳ |❓ Kurdish |HanumeshGupta |
|🔴 |🇨🇳 Traditional Chinese|[⭐ Contribute!](.github/CONTRIBUTING.md)|
<!--[⭐ Contribute!](.github/CONTRIBUTING.md) -->
## ⭐️ Star History
Please give this repository a star if you like it.
If you enjoy using Open ticket, **consider starring** this repository.
This will help us grow and reach even more people!
<a href="https://star-history.com/#open-discord-bots/open-ticket&Date">
@@ -184,13 +191,21 @@ This will help us grow and reach even more people!
</a>
## 🧩 Plugins
**Download all plugins in our [Official Plugin Repository](https://github.com/open-discord-bots/plugins)!**<br>
**Download all plugins from our [Official Plugin Repository](https://github.com/open-discord-bots/plugins)!**<br>
> #### ⭐ Featured Plugins (Top 5 Most Used)
> **[`ot-sqlite-database`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-sqlite-database/),
> [`ot-migrate-v3`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-migrate-v3/),
> [`ot-reviews`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-reviews/),
> [`ot-feedback`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-feedback/),
> [`ot-tags`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-tags/)**
> [`ot-tags`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-tags/),
> [`ot-config-reload`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-config-reload/)**
#### Categories:
- **📄 Example** - These plugins serve as an example or starting template.
- **📢 Command** - These plugins add new commands to the bot.
- **⚙️ Utility** - These plugins help with utility systems. You might not notice them as a ticket user/admin directly.
- **🎨 Customisation** - These plugins add even more customisation to the bot.
- **💼 Management** - These plugins add features that help you manage your server or ticket system.
- **🤖 Client** - These plugins add features affecting the Discord Client or bot itself.
### 📦 Official *(made by DJdj Development)*
|Name |Category |Description |
@@ -227,18 +242,10 @@ This will help us grow and reach even more people!
|[`od-reminders`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/od-reminders/) |guillee.3 |💼 Management |Set reminders that will be sent to a channel every specified time. |
|[`ot-translate-cmds`](https://github.com/open-discord-bots/plugins/tree/main/open-ticket/ot-translate-cmds/) |guillee.3 |🤖 Client |Translate all built-in command names, descriptions & options. |
### 📢 Categories
- **📄 Example** - These plugins serve as an example or starting template.
- **📢 Command** - These plugins add new commands to the bot.
- **⚙️ Utility** - These plugins help with backend & systems. You won't notice it in discord itself.
- **🎨 Customisation** - These plugins help you customise the bot even further.
- **💼 Management** - These plugins add features that help you manage your server.
- **🤖 Client** - These plugins add features affecting the discord client or bot itself.
---
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="170px">
**README.md**<br>
[changelog](https://otgithub.dj-dj.be/releases) - [documentation](https://otdocs.dj-dj.be) - [tutorial](https://www.youtube.com/watch?v=2jK9kAf6ASU) - [website](https://openticket.dj-dj.be) - [discord server](https://discord.dj-dj.be) - [license](./LICENSE.md)<br>
[Changelog](https://otgithub.dj-dj.be/releases) - [Documentation](https://otdocs.dj-dj.be) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2025 - [DJdj Development](https://www.dj-dj.be) - [Terms](https://www.dj-dj.be/terms) - [Privacy Policy](https://www.dj-dj.be/privacy) - [Support Us](https://github.com/sponsors/DJj123dj)
+51 -21
View File
@@ -2,10 +2,10 @@
"_INFO":{
"support":"https://otdocs.dj-dj.be",
"discord":"https://discord.dj-dj.be",
"version":"open-ticket-v4.0.7"
"version":"open-ticket-v4.1.0"
},
"token":"your bot token here! (or leave empty when using 'tokenFromENV')",
"token":"insert your bot token here! (or leave empty when using 'tokenFromENV')",
"tokenFromENV":false,
"mainColor":"#f8ba00",
@@ -20,21 +20,34 @@
"status":{
"enabled":true,
"type":"listening OR watching OR playing OR custom",
"mode":"online OR invisible OR idle OR dnd",
"text":"/help",
"status":"online OR invisible OR idle OR dnd"
"state":"(additional text or leave empty)"
},
"system":{
"removeParticipantsOnClose":false,
"replyOnTicketCreation":true,
"replyOnReactionRole":true,
"useTranslatedConfigChecker":true,
"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,
@@ -54,6 +67,18 @@
"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)",
@@ -73,23 +98,28 @@
"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)"
"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},
"roleAdding":{"dm":false, "logs":true},
"roleRemoving":{"dm":false, "logs":true}
"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}
}
}
}
+5 -1
View File
@@ -25,7 +25,7 @@
"claimedCategory":[
{"user":"user id","category":"category id"}
],
"description":"This is a question ticket (or leave empty)"
"topic":"This is the topic of this ticket channel and is visible to everyone! (or leave empty)"
},
"dmMessage":{
@@ -87,6 +87,10 @@
"enabled":false,
"globalMaximum":20,
"userMaximum":3
},
"slowMode":{
"enabled":false,
"slowModeSeconds":20
}
},
{
+1 -1
View File
@@ -17,7 +17,7 @@
"image":"https://www.example.com/image.png (or leave empty)",
"thumbnail":"https://www.example.com/image.png (or leave empty)",
"footer":"Open Ticket v4.0.7 (or leave empty)",
"footer":"Open Ticket v4.1.0 (or leave empty)",
"fields":[
{"name":"field name","value":"field value","inline":false}
],
+1 -1
View File
@@ -14,7 +14,7 @@ process.argv.push(...flags)
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║
╚██████╔╝██║ ███████╗██║ ╚████║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝
v4.0.7 - Made by DJj123dj & Contributors
v4.1.0 - Made by DJj123dj & Contributors
Discord: https://discord.dj-dj.be
Docs: https://otdocs.dj-dj.be
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION": {
"otversion": "v4.0.7",
"otversion": "v4.1.0",
"translators": ["palestinian"],
"lastedited": "06/12/2024",
"language": "Arabic",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Bengali",
+145 -15
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["guillee3"],
"lastedited":"21/08/2024",
"lastedited":"09/11/2025",
"language":"Catalan",
"automated":false
},
@@ -31,6 +31,14 @@
"stringContains":"Aquesta cadena ha de contenir {0}!",
"stringChoices":"Aquesta cadena només pot ser un dels valors següents: {0}!",
"stringRegex":"Aquesta cadena és invàlida!",
"stringInvertedContains":"Aquesta cadena no pot contenir {0}!",
"stringLowercase":"Aquesta cadena ha d'estar escrita només en minúscules!",
"stringUppercase":"Aquesta cadena ha d'estar escrita només en majúscules!",
"stringSpecialCharacters":"Aquesta cadena no pot contenir cap caràcter especial! (només a-z, 0-9 i espai)",
"stringNoSpaces":"Aquesta cadena no pot contenir espais!",
"stringCapitalWord":"Es recomana que cada paraula d'aquesta cadena comenci amb una lletra majúscula!",
"stringCapitalSentence":"Sembla que algunes frases d'aquesta cadena no comencen amb una lletra majúscula!",
"stringPunctuation":"Sembla que la frase d'aquesta cadena no acaba amb un signe de puntuació!",
"numberTooShort":"Aquest nombre no pot tenir menys de {0} caràcters!",
"numberTooLong":"Aquest nombre no pot tenir més de {0} caràcters!",
@@ -48,6 +56,8 @@
"numberNegative":"Aquest nombre no pot ser negatiu!",
"numberPositive":"Aquest nombre no pot ser positiu!",
"numberZero":"Aquest nombre no pot ser zero!",
"numberNan":"Aquest número no pot ser NaN (Not A Number)!",
"numberInvertedContains":"Aquest número no pot contenir {0}!",
"booleanTrue":"Aquest booleà no pot ser veritable!",
"booleanFalse":"Aquest booleà no pot ser fals!",
@@ -89,7 +99,8 @@
"invalidButton":"Aquest botó ha de tenir almenys un {0} o {1}!",
"unusedOption":"L'opció {0} no s'utilitza enlloc!",
"unusedQuestion":"La pregunta {0} no s'utilitza enlloc!",
"dropdownOption":"Un panell amb desplegable activat només pot contenir opcions del tipus 'ticket'!"
"dropdownOption":"Un panell amb desplegable activat només pot contenir opcions del tipus 'ticket'!",
"customInvalidVersion":"La versió especificada a la teva configuració no coincideix! Assegura't d'haver actualitzat la configuració a la versió més recent!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Afegit a la Llista Negra",
"blacklistRemoveDm":"Eliminat de la Llista Negra",
"clear":"Tickets Netejats",
"clearTickets":"Netejar Tickets",
"roles":"Rols Actualitzats",
"autoclose":"Ticket Tancat Automàticament",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Tancament Automàtic Desactivat",
"autodelete":"Ticket Eliminat Automàticament",
"autodeleteEnabled":"Eliminació Automàtica Activada",
"autodeleteDisabled":"Eliminació Automàtica Desactivada"
"autodeleteDisabled":"Eliminació Automàtica Desactivada",
"topicSet":"Tema Canviat",
"prioritySet":"Prioritat Canviada",
"priorityGet":"Prioritat del Ticket",
"transfer":"Ticket Transferit"
},
"descriptions":{
"create":"El teu ticket ha estat creat. Fes clic al botó a continuació per accedir-hi!",
@@ -180,7 +197,12 @@
"ticketMessageLimit":"Només pots crear {0} ticket(s) al mateix temps!",
"ticketMessageAutoclose":"Aquest ticket es tancarà automàticament quan estigui inactiu durant {0}h!",
"ticketMessageAutodelete":"Aquest ticket es eliminarà automàticament quan estigui inactiu durant {0} dies!",
"panelReady":"Pots trobar el panell a continuació!\nAquest missatge ara es pot eliminar!"
"panelReady":"El panell està disponible en el missatge de seguiment!\nAquest missatge es pot eliminar ara!",
"topicSet":"El tema del canal ha estat canviat per {0} amb èxit!",
"prioritySet":"La prioritat del ticket ha estat canviada a {0} per {1} amb èxit!",
"priorityGet":"La prioritat actual d'aquest ticket és {0}.",
"transfer":"La propietat del ticket ha estat transferida de {0} a {1} per {2} amb èxit!"
},
"modal":{
"closePlaceholder":"Per què has tancat aquest ticket?",
@@ -220,7 +242,14 @@
"blacklistRemoveLog":"{0} ha estat eliminat de la llista negra per {1}!",
"blacklistAddDm":"Has estat afegit a la llista negra al nostre servidor!\nA partir d'ara, no pots crear cap ticket!",
"blacklistRemoveDm":"Has estat eliminat de la llista negra al nostre servidor!\nAra pots crear tickets de nou!",
"clearLog":"{0} tickets han estat eliminats per {1}!"
"clearLog":"{0} tickets han estat eliminats per {1}!",
"transferLog":"La propietat d'aquest ticket ha estat transferida de {0} a {1} per {2}!",
"transferDm":"La propietat del teu ticket ha estat transferida de {0} a {1} al nostre servidor!",
"prioritySetLog":"La prioritat d'aquest ticket ha estat canviada a {0} per {1}!",
"prioritySetDm":"La prioritat del teu ticket ha estat canviada a {0} al nostre servidor!",
"roleUpdateLog":"{0} ha actualitzat els seus rols!",
"roleUpdateDm":"Els teus rols al nostre servidor han estat actualitzats!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"retry":"Torna-ho a intentar",
"continue":"Elimina Sense Transcripció",
"backup":"Crea Transcripció de Backup",
"error":"Alguna cosa ha anat malament mentre intentàvem crear la transcripció.\nQuè t'agradaria fer?\n\nAquest ticket no s'eliminarà fins que facis clic en un d'aquests botons."
"error":"Alguna cosa ha anat malament mentre intentàvem crear la transcripció.\nQuè t'agradaria fer?\n\nAquest ticket no s'eliminarà fins que facis clic en un d'aquests botons.",
"title":"Error de transcripció"
},
"text":{
"messagesTitle":"MISSATGES",
"embedTitle":"EMBED",
"fileTitle":"FITXER",
"fieldsTitle":"CAMPS",
"reactionsTitle":"REACCIONS",
"statsTitle":"ESTADÍSTIQUES",
"emptyContent":"<el contingut està buit>",
"noTitle":"<sense títol>",
"noDesc":"<sense descripció>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Panell Desconegut",
"notInGuild":"No Ets Al Servidor",
"channelRename":"No Es Pot Canviar El Nom Del Canal",
"busy":"El Ticket Està Ocupat"
"busy":"El Ticket Està Ocupat",
"permissionError":"Error de Permisos"
},
"descriptions":{
"askForInfo":"Contacta amb el propietari d'aquest bot per obtenir més informació!",
@@ -280,7 +322,10 @@
"notInGuild":"Aquest {0} no funciona en DM! Si us plau, torna a intentar-ho en un servidor!",
"channelRename":"Degut a les limitacions de discord, actualment és impossible per al bot canviar el nom del canal. El canal es canviarà automàticament en 10 minuts si el bot no es reinicia.",
"channelRenameSource":"La font d'aquest error és: {0}",
"busy":"No es pot utilitzar aquest {0}!\nEl ticket està sent processat pel bot.\n\nSi us plau, torna a intentar-ho en uns segons!"
"busy":"No es pot utilitzar aquest {0}!\nEl ticket està sent processat pel bot.\n\nSi us plau, torna a intentar-ho en uns segons!",
"closeBeforeMessage":"Aquest ticket no pot ser tancat/eliminat abans que un usuari hagi enviat un missatge.",
"closeBeforeAdminMessage":"Aquest ticket no pot ser tancat/eliminat abans que un administrador o membre de suport hagi enviat un missatge.",
"unableToCreateTicket":"No pots crear cap ticket."
},
"optionInvalidReasons":{
"stringRegex":"El valor no coincideix amb el patró!",
@@ -331,7 +376,6 @@
"added":"Afegit",
"removed":"Eliminat",
"filter":"Filtre",
"claimedBy":"Reclamat Per {0}",
"method":"Mètode",
"type":"Tipus",
"blacklisted":"Llista Negra",
@@ -355,7 +399,27 @@
"status":"Estat",
"claimed":"Reclamat",
"pinned":"Fixat",
"creationDate":"Data de Creació"
"creationDate":"Data de Creació",
"noone":"Ningú",
"open":"Obert",
"closed":"Tancat",
"priority":"Prioritat",
"participants":"Participants",
"yes":"Si",
"no":"No",
"option":"Opció",
"topic":"Tema",
"uptime":"Temps d'Activitat del Sistema",
"messages":"Missatges",
"embeds":"Embeds",
"files":"Fitxers",
"components":"Components",
"cooldown":"Refredament",
"maxTickets":"Max Tickets",
"admins":"Admins",
"roles":"Rols",
"size":"Mida"
},
"lowercase":{
"text":"text",
@@ -384,6 +448,7 @@
"unclaim":"Allibera un ticket.",
"pin":"Fixa un ticket.",
"unpin":"Desfixa un ticket.",
"move":"Mou un ticket.",
"moveId":"L'identificador de l'opció a la qual vols moure't.",
"rename":"Renomena un ticket.",
@@ -392,6 +457,7 @@
"addUser":"L'usuari a afegir.",
"remove":"Elimina un usuari d'un ticket.",
"removeUser":"L'usuari a eliminar.",
"blacklist":"Gestiona la llista negra de tickets.",
"blacklistView":"Veure una llista de l'actual llista negra.",
"blacklistAdd":"Afegeix un usuari a la llista negra.",
@@ -405,6 +471,7 @@
"statsUserUser":"L'usuari del qual vols veure les estadístiques.",
"statsTicket":"Veure les estadístiques d'un ticket al servidor.",
"statsTicketTicket":"El ticket que vols veure.",
"clear":"Elimina múltiples tickets al mateix temps.",
"clearFilter":"El filtre per a la neteja de tickets.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Desfixats",
"autoclose":"Tancat Automàticaments"
},
"autoclose":"Gestiona el tancament automàtic en un ticket.",
"autocloseDisable":"Desactiva el tancament automàtic en aquest ticket.",
"autocloseEnable":"Activa el tancament automàtic en aquest ticket.",
@@ -424,7 +492,19 @@
"autodelete":"Gestiona l'eliminació automàtica en un ticket.",
"autodeleteDisable":"Desactiva l'eliminació automàtica en aquest ticket.",
"autodeleteEnable":"Activa l'eliminació automàtica en aquest ticket.",
"autodeleteEnableTime":"La quantitat de dies que aquest ticket ha d'estar inactiu per eliminar-lo."
"autodeleteEnableTime":"La quantitat de dies que aquest ticket ha d'estar inactiu per eliminar-lo.",
"topic":"Gestiona el tema del canal del ticket.",
"topicSet":"Estableix el tema del canal del ticket.",
"topicValue":"El nou tema del canal.",
"topicList":"Obté una llista de tots els tickets amb el seu tema i estadístiques.",
"priority":"Gestiona la prioritat del ticket.",
"prioritySet":"Estableix la prioritat del ticket.",
"priorityValue":"La prioritat del canal.",
"priorityGet":"Obtenir la prioritat del ticket.",
"priorityList":"Obté una llista de tots els tickets amb el seu estat de prioritat.",
"transfer":"Transfereix la propietat del ticket d'un usuari a un altre.",
"transferUser":"L'usuari al qual transferir."
},
"helpMenu":{
"help":"Obtingues una llista de totes les comandes disponibles.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Desactiva el tancament automàtic en aquest ticket.",
"autocloseEnable":"Activa el tancament automàtic en aquest ticket.",
"autodeleteDisable":"Desactiva l'eliminació automàtica en aquest ticket.",
"autodeleteEnable":"Activa l'eliminació automàtica en aquest ticket."
"autodeleteEnable":"Activa l'eliminació automàtica en aquest ticket.",
"categories":{
"general":"Comandes Generals",
"basicTicket":"Comandes Bàsiques de Tickets",
"advancedTicket":"Comandes Avançades de Tickets",
"userTicket":"Comandes de Tickets d'Usuari",
"admin":"Comandes d'Admin",
"advanced":"Comandes Avançades",
"extra":"Comandes Extra"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"Estadístiques del Sistema",
"user":"Estadístiques d'Usuari",
"ticket":"Estadístiques de Ticket",
"participants":"Participants"
"participants":"Participants",
"messages":"Missatges"
},
"properties":{
"ticketsCreated":"Tickets Creats",
@@ -472,7 +562,47 @@
"ticketsPinned":"Tickets Fixats",
"ticketsMoved":"Tickets Moguts",
"usersBlacklisted":"Usuaris a la Llista Negra",
"transcriptsCreated":"Transcripcions Creades"
"transcriptsCreated":"Transcripcions Creades",
"ticketsAutodeleted":"Tickets Eliminats Automàticament",
"ticketsTransferred":"Tickets Transferits",
"ticketVolume":"Volum de Tickets",
"averageTickets":"Mitjana de Tickets/Usuari",
"currentTickets":"Tickets Actuals",
"age":"Edat del Ticket",
"responseTime":"Temps de Resposta",
"resolutionTime":"Temps de Resolució",
"createdOn":"Creat el",
"createdBy":"Creat per",
"closedOn":"Tancat el",
"closedBy":"Tancat per",
"claimedOn":"Reclamat el",
"claimedBy":"Reclamat per",
"pinnedOn":"Fixat el",
"pinnedBy":"Fixat per",
"deletedOn":"Eliminat el",
"deletedBy":"Eliminat per"
},
"roles":{
"developer":"Desenvolupador",
"serverOwner":"Propietari del Servidor",
"serverAdmin":"Admin del Servidor",
"moderator":"Equip de Moderació",
"support":"Equip de Suport",
"member":"Membre"
}
},
"panel":{
"selectTicket":"Selecciona el teu ticket",
"selectRole":"Selecciona el teu rol",
"selectOption":"Selecciona la teva opció"
},
"priorities":{
"urgent":"Urgent",
"veryHigh":"Molt Alt",
"high":"Alt",
"normal":"Normal",
"low":"Baix",
"veryLow":"Molt Baix",
"none":"Cap"
}
}
+145 -15
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["DJj123dj"],
"lastedited":"21/08/2024",
"lastedited":"09/11/2025",
"language":"Custom",
"automated":false
},
@@ -31,6 +31,14 @@
"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!",
"numberTooShort":"This number can't be shorter than {0} characters!",
"numberTooLong":"This number can't be longer than {0} characters!",
@@ -48,6 +56,8 @@
"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}!",
"booleanTrue":"This boolean can't be true!",
"booleanFalse":"This boolean can't be false!",
@@ -89,7 +99,8 @@
"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!"
"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!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Added To Blacklist",
"blacklistRemoveDm":"Removed From Blacklist",
"clear":"Tickets Cleared",
"clearTickets":"Clear Tickets",
"roles":"Roles Updated",
"autoclose":"Ticket Autoclosed",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Autoclose Disabled",
"autodelete":"Ticket Autodeleted",
"autodeleteEnabled":"Autodelete Enabled",
"autodeleteDisabled":"Autodelete Disabled"
"autodeleteDisabled":"Autodelete Disabled",
"topicSet":"Topic Changed",
"prioritySet":"Priority Changed",
"priorityGet":"Ticket Priority",
"transfer":"Ticket Transferred"
},
"descriptions":{
"create":"Your ticket has been created. Click the button below to access it!",
@@ -180,7 +197,12 @@
"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":"You can find the panel below!\nThis message can now be deleted!"
"panelReady":"The panel is available in the followup message!\nThis message can now be deleted!",
"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!"
},
"modal":{
"closePlaceholder":"Why did you close this ticket?",
@@ -220,7 +242,14 @@
"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}!"
"clearLog":"{0} tickets 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!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"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 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.",
"title":"Transcript Error"
},
"text":{
"messagesTitle":"MESSAGES",
"embedTitle":"EMBED",
"fileTitle":"FILE",
"fieldsTitle":"FIELDS",
"reactionsTitle":"REACTIONS",
"statsTitle":"STATS",
"emptyContent":"<content is empty>",
"noTitle":"<no-title>",
"noDesc":"<no-description>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Unknown Panel",
"notInGuild":"Not In Server",
"channelRename":"Unable To Rename Channel",
"busy":"Ticket Is Busy"
"busy":"Ticket Is Busy",
"permissionError":"Permission Error"
},
"descriptions":{
"askForInfo":"Contact the owner of this bot for more info!",
@@ -280,7 +322,10 @@
"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!"
"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."
},
"optionInvalidReasons":{
"stringRegex":"Value doesn't match pattern!",
@@ -331,7 +376,6 @@
"added":"Added",
"removed":"Removed",
"filter":"Filter",
"claimedBy":"Claimed By {0}",
"method":"Method",
"type":"Type",
"blacklisted":"Blacklisted",
@@ -355,7 +399,27 @@
"status":"Status",
"claimed":"Claimed",
"pinned":"Pinned",
"creationDate":"Creation Date"
"creationDate":"Creation Date",
"noone":"No-One",
"open":"Open",
"closed":"Closed",
"priority":"Priority",
"participants":"Participants",
"yes":"Yes",
"no":"No",
"option":"Option",
"topic":"Topic",
"uptime":"System Uptime",
"messages":"Messages",
"embeds":"Embeds",
"files":"Files",
"components":"Components",
"cooldown":"Cooldown",
"maxTickets":"Max Tickets",
"admins":"Admins",
"roles":"Roles",
"size":"Size"
},
"lowercase":{
"text":"text",
@@ -384,6 +448,7 @@
"unclaim":"Unclaim a ticket.",
"pin":"Pin a ticket.",
"unpin":"Unpin a ticket.",
"move":"Move a ticket.",
"moveId":"The identifier of the option that you want to move to.",
"rename":"Rename a ticket.",
@@ -392,6 +457,7 @@
"addUser":"The user to add.",
"remove":"Remove a user from a ticket.",
"removeUser":"The user to remove.",
"blacklist":"Manage the ticket blacklist.",
"blacklistView":"View a list of the current blacklist.",
"blacklistAdd":"Add a user to the blacklist.",
@@ -405,6 +471,7 @@
"statsUserUser":"The user to view.",
"statsTicket":"View the stats of a ticket in the server.",
"statsTicketTicket":"The ticket to view.",
"clear":"Delete multiple tickets at the same time.",
"clearFilter":"The filter for clearing tickets.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Unpinned",
"autoclose":"Autoclosed"
},
"autoclose":"Manage autoclose in a ticket.",
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
@@ -424,7 +492,19 @@
"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."
"autodeleteEnableTime":"The amount of days this ticket needs to be inactive to delete it.",
"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."
},
"helpMenu":{
"help":"Get a list of all the available commands.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
"autodeleteDisable":"Disable autodelete in this ticket.",
"autodeleteEnable":"Enable autodelete in this ticket."
"autodeleteEnable":"Enable autodelete in this ticket.",
"categories":{
"general":"General Commands",
"basicTicket":"Basic Ticket Commands",
"advancedTicket":"Advanced Ticket Commands",
"userTicket":"User Ticket Commands",
"admin":"Admin Commands",
"advanced":"Advanced Commands",
"extra":"Extra Commands"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"System Stats",
"user":"User Stats",
"ticket":"Ticket Stats",
"participants":"Participants"
"participants":"Participants",
"messages":"Messages"
},
"properties":{
"ticketsCreated":"Tickets Created",
@@ -472,7 +562,47 @@
"ticketsPinned":"Tickets Pinned",
"ticketsMoved":"Tickets Moved",
"usersBlacklisted":"Users Blacklisted",
"transcriptsCreated":"Transcripts Created"
"transcriptsCreated":"Transcripts Created",
"ticketsAutodeleted":"Tickets Autodeleted",
"ticketsTransferred":"Tickets Transferred",
"ticketVolume":"Ticket Volume",
"averageTickets":"Average Tickets/User",
"currentTickets":"Current Tickets",
"age":"Ticket Age",
"responseTime":"Response Time",
"resolutionTime":"Resolution Time",
"createdOn":"Created On",
"createdBy":"Created By",
"closedOn":"Closed On",
"closedBy":"Closed By",
"claimedOn":"Claimed On",
"claimedBy":"Claimed By",
"pinnedOn":"Pinned On",
"pinnedBy":"Pinned By",
"deletedOn":"Deleted On",
"deletedBy":"Deleted By"
},
"roles":{
"developer":"Developer",
"serverOwner":"Server Owner",
"serverAdmin":"Server Admin",
"moderator":"Moderator Team",
"support":"Support Team",
"member":"Member"
}
},
"panel":{
"selectTicket":"Select your ticket",
"selectRole":"Select your role",
"selectOption":"Select your option"
},
"priorities":{
"urgent":"Urgent",
"veryHigh":"Very High",
"high":"High",
"normal":"Normal",
"low":"Low",
"veryLow":"Very Low",
"none":"None"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["spyeye_"],
"lastedited":"21/08/2024",
"language":"Czech",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["the_gamer"],
"lastedited":"26/09/2024",
"language":"Danish",
+149 -19
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["DJj123dj"],
"lastedited":"21/08/2024",
"lastedited":"09/11/2025",
"language":"Dutch",
"automated":false
},
@@ -31,6 +31,14 @@
"stringContains":"Deze tekst moet {0} bevatten!",
"stringChoices":"Deze tekst kan alleen de volgende waardes bevatten: {0}!",
"stringRegex":"Deze tekst is ongeldig!",
"stringInvertedContains":"Deze tekst mag niet {0} bevatten!",
"stringLowercase":"Deze tekst moet in kleine letters geschreven zijn!",
"stringUppercase":"Deze tekst moet in hoofdletters geschreven zijn!",
"stringSpecialCharacters":"Deze tekst mag geen speciale karakters bevatten! (enkel a-z, 0-9 & spatie)",
"stringNoSpaces":"Deze tekst mag geen spaties bevatten!",
"stringCapitalWord":"Het is aangeraden om ieder woord in de zin te laten starten met een hoofdletter!",
"stringCapitalSentence":"Het ziet er naar uit dat sommige zinnen in deze tekst niet met een hoofdletter beginnen!",
"stringPunctuation":"Het ziet er naar uit dat de zin in deze tekst niet met een leesteken eindigt!",
"numberTooShort":"Dit nummer kan niet korter dan {0} karakters zijn!",
"numberTooLong":"Dit nummer kan niet langer dan {0} karakters zijn!",
@@ -48,6 +56,8 @@
"numberNegative":"Dit nummer kan niet negatief zijn!",
"numberPositive":"Dit nummer kan niet positief zijn!",
"numberZero":"Dit nummer kan niet nul zijn!",
"numberNan":"Dit nummer kan geen NaN (Not A Number) zijn!",
"numberInvertedContains":"Dit nummer mag niet {0} bevatten!",
"booleanTrue":"Deze boolean kan niet true zijn!",
"booleanFalse":"Deze boolean kan niet false zijn!",
@@ -89,7 +99,8 @@
"invalidButton":"Deze knop moet een {0} of {1} hebben!",
"unusedOption":"De optie {0} wordt nergens gebruikt!",
"unusedQuestion":"The vraag {0} wordt nergens gebruikt!",
"dropdownOption":"Een paneel met dropdown mag alleen opties bevaten van het 'ticket' type!"
"dropdownOption":"Een paneel met dropdown mag alleen opties bevaten van het 'ticket' type!",
"customInvalidVersion":"De versie gespecificeerd in u configbestand komt niet overeen! Zorg dat u het configbestand geüpdatet heeft naar de laatste versie!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Toegevoegd Aan Blacklist",
"blacklistRemoveDm":"Verwijderd Van Blacklist",
"clear":"Tickets Opgeruimd",
"clearTickets":"Tickets Opruimen",
"roles":"Rollen Bijgewerkt",
"autoclose":"Ticket Automatisch Gesloten",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Autoclose Uitgeschakeld",
"autodelete":"Ticket Automatisch Verwijderd",
"autodeleteEnabled":"Autodelete Ingeschakeld",
"autodeleteDisabled":"Autodelete Uitgeschakeld"
"autodeleteDisabled":"Autodelete Uitgeschakeld",
"topicSet":"Onderwerp Veranderd",
"prioritySet":"Prioriteit Veranderd",
"priorityGet":"Ticket Prioriteit",
"transfer":"Ticket Overgedragen"
},
"descriptions":{
"create":"Je ticket is aangemaakt. Klik op de knop hier onder om er naar toe te gaan!",
@@ -169,18 +186,23 @@
"rolesEmpty":"Geen enkele rollen zijn bijgewerkt!",
"autocloseLeave":"Dit ticket is automatisch gesloten omdat de maker de server verlaten is!",
"autocloseTimeout":"Dit ticket is automatisch gesloten omdat het inactief was voor meer dan `{0} uur`!",
"autocloseTimeout":"Dit ticket is automatisch gesloten omdat het meer dan `{0} uur` inactief was!",
"autodeleteLeave":"Dit ticket is automatisch verwijderd omdat de maker de server verlaten is!",
"autodeleteTimeout":"Dit ticket is automatisch verwijderd omdat het inactief was voor meer dan `{0} dagen`!",
"autocloseEnabled":"Autoclose is ingeschakeld in dit ticket!\nHet wordt gesloten wanneer het inactief is voor meer dan `{0} uur`!",
"autodeleteTimeout":"Dit ticket is automatisch verwijderd omdat het meer dan `{0} dagen` inactief was!",
"autocloseEnabled":"Autoclose is ingeschakeld in dit ticket!\nHet wordt gesloten na `{0} uur` inactiviteit!",
"autocloseDisabled":"Autoclose is uitgeschakeld in dit ticket!\nHet wordt niet meer automatisch gesloten!",
"autodeleteEnabled":"Autodelete is ingeschakeld in dit ticket!\nHet wordt verwijderd wanneer het inactief is voor meer dan `{0} dagen`!",
"autodeleteEnabled":"Autodelete is ingeschakeld in dit ticket!\nHet wordt verwijderd na `{0} dagen` inactiviteit!",
"autodeleteDisabled":"Autodelete is uitgeschakeld in dit ticket!\nHet wordt niet meer automatisch verwijderd!",
"ticketMessageLimit":"Je kan maar {0} ticket(s) op het zelfde moment aanmaken!",
"ticketMessageAutoclose":"Dit ticket wordt automatisch gesloten wanneer het inactief is voor {0} uur!",
"ticketMessageAutodelete":"Dit ticket wordt automatisch verwijderd wanneer het inactief is voor {0} dagen!",
"panelReady":"Je kan het paneel hier onder vinden!\nDit bericht kan nu verwijderd worden!"
"panelReady":"Het paneel is beschikbaar in het volgende bericht!\nDit bericht kan verwijderd worden!",
"topicSet":"Het kanaalsonderwerp is succesvol veranderd naar {0}!",
"prioritySet":"De ticket prioriteit is succesvol veranderd naar {0} door {1}!",
"priorityGet":"De huidige prioriteit van dit ticket is {0}.",
"transfer":"Het ticket is succesvol overgedragen van {0} naar {1} door {2}!"
},
"modal":{
"closePlaceholder":"Waarom heb je dit ticket gesloten?",
@@ -220,7 +242,14 @@
"blacklistRemoveLog":"{0} is verwijderd van de blacklist door {1}!",
"blacklistAddDm":"Je bent geblacklist in onze server!\nVanaf nu kan je geen tickets meer aanmaken!",
"blacklistRemoveDm":"Je bent verwijderd van de blacklist in onze server!\nNormaal zou je nu terug tickets kunnen aanmaken!",
"clearLog":"{0} tickets zijn verwijderd door by {1}!"
"clearLog":"{0} tickets zijn verwijderd door by {1}!",
"transferLog":"Het ticket is overgedragen van {0} naar {1} door {2}!",
"transferDm":"U ticket is overgedragen van {0} naar {1} in onze server!",
"prioritySetLog":"De prioriteit van dit ticket is veranderd naar {0} door {1}!",
"prioritySetDm":"De prioriteit van u ticket is veranderd naar {0} in onze server!",
"roleUpdateLog":"{0} heeft zijn/haar rollen bewerkt!",
"roleUpdateDm":"U rollen in onze server zijn veranderd!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"retry":"Herprobeer",
"continue":"Verwijder Zonder Transcript",
"backup":"Maak Backup Transcript",
"error":"Er is iets misgegaan bij het maken van het transcript.\nWat wil je doen?\n\nDit ticket wordt niet meer verwijderd to je een van de onderstaande knoppen gebruikt."
"error":"Er is iets misgegaan bij het maken van het transcript.\nWat wil je doen?\n\nDit ticket wordt niet meer verwijderd to je een van de onderstaande knoppen gebruikt.",
"title":"Transcript Error"
},
"text":{
"messagesTitle":"BERICHTEN",
"embedTitle":"EMBED",
"fileTitle":"BESTAND",
"fieldsTitle":"VELDEN",
"reactionsTitle":"REACTIES",
"statsTitle":"STATS",
"emptyContent":"<inhoud is leeg>",
"noTitle":"<geen-titel>",
"noDesc":"<geen-beschrijving>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Onbekend Paneel",
"notInGuild":"Niet In Server",
"channelRename":"Kan Kanaal Niet Hernoemen",
"busy":"Ticket Is Bezig"
"busy":"Ticket Is Bezig",
"permissionError":"Permission Error"
},
"descriptions":{
"askForInfo":"Contacteer the eigenaar van deze bot voor meer info!",
@@ -280,7 +322,10 @@
"notInGuild":"Deze {0} werkt niet in DM! Probeer het opnieuw in een server!",
"channelRename":"Door discord ratelimits is het op dit moment onmogelijk voor de bot om het kanaal te hernoemen. Het kanaal zal automatisch hernoemd worden over 10 min als de bot niet herstart wordt.",
"channelRenameSource":"De bron van deze error is: {0}",
"busy":"Kan deze {0} niet gebruiken!\nHet ticket wordt op dit moment verwerkt door de bot.\n\nProbeer het opnieuw binnen een paar seconden!"
"busy":"Kan deze {0} niet gebruiken!\nHet ticket wordt op dit moment verwerkt door de bot.\n\nProbeer het opnieuw binnen een paar seconden!",
"closeBeforeMessage":"Dit ticket kan niet gesloten/verwijderd worden vooraleer er een bericht door een gebruiker verstuurd is.",
"closeBeforeAdminMessage":"Dit ticket kan niet gesloten/verwijderd worden vooraleer er een bericht door een ticket admin of supportlid verstuurd is.",
"unableToCreateTicket":"U kunt geen ticket aanmaken."
},
"optionInvalidReasons":{
"stringRegex":"Waarde is niet gelijk aan het patroon!",
@@ -331,7 +376,6 @@
"added":"Toegevoegd",
"removed":"Verwijderd",
"filter":"Filter",
"claimedBy":"Geclaimd Door {0}",
"method":"Methode",
"type":"Type",
"blacklisted":"Geblacklist",
@@ -355,7 +399,27 @@
"status":"Status",
"claimed":"Geclaimd",
"pinned":"Gepind",
"creationDate":"Aanmaakdatum"
"creationDate":"Aanmaakdatum",
"noone":"Niemand",
"open":"Open",
"closed":"Gesloten",
"priority":"Prioriteit",
"participants":"Deelnemers",
"yes":"Ja",
"no":"Nee",
"option":"Optie",
"topic":"Onderwerp",
"uptime":"Systeem Uptime",
"messages":"Berichten",
"embeds":"Embeds",
"files":"Bestanden",
"components":"Componenten",
"cooldown":"Cooldown",
"maxTickets":"Max Tickets",
"admins":"Admins",
"roles":"Rollen",
"size":"Grootte"
},
"lowercase":{
"text":"tekst",
@@ -384,6 +448,7 @@
"unclaim":"Ontclaim dit ticket.",
"pin":"Pin dit ticket.",
"unpin":"Maak dit ticket los.",
"move":"Verplaats een ticket.",
"moveId":"Het id van de optie waarnaar je wilt verplaatsen.",
"rename":"Hernoem een ticket.",
@@ -392,6 +457,7 @@
"addUser":"De gebruiker om toe te voegen.",
"remove":"Verwijder een user van dit ticket.",
"removeUser":"De gebruiker om te verwijderen.",
"blacklist":"Beheer de ticket blacklist.",
"blacklistView":"Bekijk de huidige blacklist.",
"blacklistAdd":"Voeg een gebruiker toe aan de blacklist.",
@@ -405,6 +471,7 @@
"statsUserUser":"De gebruiker om te bekijken.",
"statsTicket":"Bekijk de stats van een ticket in de server.",
"statsTicketTicket":"Het ticket om te bekijken.",
"clear":"Verwijder meerdere tickets tegelijkertijd.",
"clearFilter":"De filter om tickets op te ruimen.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Losgemaakt (niet gepind)",
"autoclose":"Automatisch Gesloten"
},
"autoclose":"Beheer autoclose in een ticket.",
"autocloseDisable":"Schakel autoclose in dit ticket uit.",
"autocloseEnable":"Schakel autoclose in dit ticket in.",
@@ -424,7 +492,19 @@
"autodelete":"Beheer autodelete in een ticket.",
"autodeleteDisable":"Schakel autodelete in dit ticket aan.",
"autodeleteEnable":"Schakel autodelete in dit ticket aan.",
"autodeleteEnableTime":"De hoeveelheid dagen dat dit ticket inactief moet zijn voor het te verwijderen."
"autodeleteEnableTime":"De hoeveelheid dagen dat dit ticket inactief moet zijn voor het te verwijderen.",
"topic":"Beheer het onderwerp van een ticket kanaal.",
"topicSet":"Verander het onderwerp van een ticket kanaal.",
"topicValue":"Het nieuwe onderwerp van het kanaal.",
"topicList":"Bekijk een lijst van alle tickets met hun onderwerp en stats.",
"priority":"Beheer de prioriteit van een ticket.",
"prioritySet":"Verander de prioriteit van een ticket.",
"priorityValue":"De prioriteit van het kanaal.",
"priorityGet":"Bekijk de prioriteit van een ticket.",
"priorityList":"Bekijk een lijst van alle tickets met hun prioriteit.",
"transfer":"Draag het ticket eigendom over van de ene naar de andere gebruiker.",
"transferUser":"De user om naar over te dragen."
},
"helpMenu":{
"help":"Krijg een lijst van alle beschikbare commands.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Zet autoclose uit in dit ticket.",
"autocloseEnable":"Zet autoclose in aan dit ticket.",
"autodeleteDisable":"Zet autodelete uit in dit ticket.",
"autodeleteEnable":"Zet autodelete aan in dit ticket."
"autodeleteEnable":"Zet autodelete aan in dit ticket.",
"categories":{
"general":"Algemene Commands",
"basicTicket":"Basis Ticket Commands",
"advancedTicket":"Geavanceerde Ticket Commands",
"userTicket":"Gebruiker Ticket Commands",
"admin":"Admin Commands",
"advanced":"Geavanceerde Commands",
"extra":"Extra Commands"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"Systeem Stats",
"user":"User Stats",
"ticket":"Ticket Stats",
"participants":"Deelnemers"
"participants":"Deelnemers",
"messages":"Berichten"
},
"properties":{
"ticketsCreated":"Tickets Aangemaakt",
@@ -472,7 +562,47 @@
"ticketsPinned":"Tickets Gepind",
"ticketsMoved":"Tickets Verplaatst",
"usersBlacklisted":"Users Geblacklist",
"transcriptsCreated":"Transcripts Aangemaakt"
"transcriptsCreated":"Transcripts Aangemaakt",
"ticketsAutodeleted":"Tickets Autodeleted",
"ticketsTransferred":"Tickets Overgedragen",
"ticketVolume":"Ticket Volume",
"averageTickets":"Gemiddelde Tickets/Gebruiker",
"currentTickets":"Huidige Tickets",
"age":"Ticket Leeftijd",
"responseTime":"Antwoordtijd",
"resolutionTime":"Oplostijd",
"createdOn":"Gemaakt Op",
"createdBy":"Gemaakt Door",
"closedOn":"Gesloten Op",
"closedBy":"Gesloten Door",
"claimedOn":"Geclaimed Op",
"claimedBy":"Geclaimed Door",
"pinnedOn":"Gepint Op",
"pinnedBy":"Gepint Door",
"deletedOn":"Verwijderd Op",
"deletedBy":"Verwijderd Door"
},
"roles":{
"developer":"Developer",
"serverOwner":"Server Eigenaar",
"serverAdmin":"Server Admin",
"moderator":"Moderator Team",
"support":"Support Team",
"member":"Serverlid"
}
},
"panel":{
"selectTicket":"Selecteer u ticket",
"selectRole":"Selecteer u rol",
"selectOption":"Selecteer u optie"
},
"priorities":{
"urgent":"Urgent",
"veryHigh":"Zeer Hoog",
"high":"Hoog",
"normal":"Normaal",
"low":"Laag",
"veryLow":"Zeer Laag",
"none":"Geen"
}
}
+145 -15
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["DJj123dj"],
"lastedited":"21/08/2024",
"lastedited":"09/11/2025",
"language":"English",
"automated":false
},
@@ -31,6 +31,14 @@
"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!",
"numberTooShort":"This number can't be shorter than {0} characters!",
"numberTooLong":"This number can't be longer than {0} characters!",
@@ -48,6 +56,8 @@
"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}!",
"booleanTrue":"This boolean can't be true!",
"booleanFalse":"This boolean can't be false!",
@@ -89,7 +99,8 @@
"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!"
"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!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Added To Blacklist",
"blacklistRemoveDm":"Removed From Blacklist",
"clear":"Tickets Cleared",
"clearTickets":"Clear Tickets",
"roles":"Roles Updated",
"autoclose":"Ticket Autoclosed",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Autoclose Disabled",
"autodelete":"Ticket Autodeleted",
"autodeleteEnabled":"Autodelete Enabled",
"autodeleteDisabled":"Autodelete Disabled"
"autodeleteDisabled":"Autodelete Disabled",
"topicSet":"Topic Changed",
"prioritySet":"Priority Changed",
"priorityGet":"Ticket Priority",
"transfer":"Ticket Transferred"
},
"descriptions":{
"create":"Your ticket has been created. Click the button below to access it!",
@@ -180,7 +197,12 @@
"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":"You can find the panel below!\nThis message can now be deleted!"
"panelReady":"The panel is available in the followup message!\nThis message can now be deleted!",
"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!"
},
"modal":{
"closePlaceholder":"Why did you close this ticket?",
@@ -220,7 +242,14 @@
"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}!"
"clearLog":"{0} tickets 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!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"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 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.",
"title":"Transcript Error"
},
"text":{
"messagesTitle":"MESSAGES",
"embedTitle":"EMBED",
"fileTitle":"FILE",
"fieldsTitle":"FIELDS",
"reactionsTitle":"REACTIONS",
"statsTitle":"STATS",
"emptyContent":"<content is empty>",
"noTitle":"<no-title>",
"noDesc":"<no-description>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Unknown Panel",
"notInGuild":"Not In Server",
"channelRename":"Unable To Rename Channel",
"busy":"Ticket Is Busy"
"busy":"Ticket Is Busy",
"permissionError":"Permission Error"
},
"descriptions":{
"askForInfo":"Contact the owner of this bot for more info!",
@@ -280,7 +322,10 @@
"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!"
"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."
},
"optionInvalidReasons":{
"stringRegex":"Value doesn't match pattern!",
@@ -331,7 +376,6 @@
"added":"Added",
"removed":"Removed",
"filter":"Filter",
"claimedBy":"Claimed By {0}",
"method":"Method",
"type":"Type",
"blacklisted":"Blacklisted",
@@ -355,7 +399,27 @@
"status":"Status",
"claimed":"Claimed",
"pinned":"Pinned",
"creationDate":"Creation Date"
"creationDate":"Creation Date",
"noone":"No-One",
"open":"Open",
"closed":"Closed",
"priority":"Priority",
"participants":"Participants",
"yes":"Yes",
"no":"No",
"option":"Option",
"topic":"Topic",
"uptime":"System Uptime",
"messages":"Messages",
"embeds":"Embeds",
"files":"Files",
"components":"Components",
"cooldown":"Cooldown",
"maxTickets":"Max Tickets",
"admins":"Admins",
"roles":"Roles",
"size":"Size"
},
"lowercase":{
"text":"text",
@@ -384,6 +448,7 @@
"unclaim":"Unclaim a ticket.",
"pin":"Pin a ticket.",
"unpin":"Unpin a ticket.",
"move":"Move a ticket.",
"moveId":"The identifier of the option that you want to move to.",
"rename":"Rename a ticket.",
@@ -392,6 +457,7 @@
"addUser":"The user to add.",
"remove":"Remove a user from a ticket.",
"removeUser":"The user to remove.",
"blacklist":"Manage the ticket blacklist.",
"blacklistView":"View a list of the current blacklist.",
"blacklistAdd":"Add a user to the blacklist.",
@@ -405,6 +471,7 @@
"statsUserUser":"The user to view.",
"statsTicket":"View the stats of a ticket in the server.",
"statsTicketTicket":"The ticket to view.",
"clear":"Delete multiple tickets at the same time.",
"clearFilter":"The filter for clearing tickets.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Unpinned",
"autoclose":"Autoclosed"
},
"autoclose":"Manage autoclose in a ticket.",
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
@@ -424,7 +492,19 @@
"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."
"autodeleteEnableTime":"The amount of days this ticket needs to be inactive to delete it.",
"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."
},
"helpMenu":{
"help":"Get a list of all the available commands.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Disable autoclose in this ticket.",
"autocloseEnable":"Enable autoclose in this ticket.",
"autodeleteDisable":"Disable autodelete in this ticket.",
"autodeleteEnable":"Enable autodelete in this ticket."
"autodeleteEnable":"Enable autodelete in this ticket.",
"categories":{
"general":"General Commands",
"basicTicket":"Basic Ticket Commands",
"advancedTicket":"Advanced Ticket Commands",
"userTicket":"User Ticket Commands",
"admin":"Admin Commands",
"advanced":"Advanced Commands",
"extra":"Extra Commands"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"System Stats",
"user":"User Stats",
"ticket":"Ticket Stats",
"participants":"Participants"
"participants":"Participants",
"messages":"Messages"
},
"properties":{
"ticketsCreated":"Tickets Created",
@@ -472,7 +562,47 @@
"ticketsPinned":"Tickets Pinned",
"ticketsMoved":"Tickets Moved",
"usersBlacklisted":"Users Blacklisted",
"transcriptsCreated":"Transcripts Created"
"transcriptsCreated":"Transcripts Created",
"ticketsAutodeleted":"Tickets Autodeleted",
"ticketsTransferred":"Tickets Transferred",
"ticketVolume":"Ticket Volume",
"averageTickets":"Average Tickets/User",
"currentTickets":"Current Tickets",
"age":"Ticket Age",
"responseTime":"Response Time",
"resolutionTime":"Resolution Time",
"createdOn":"Created On",
"createdBy":"Created By",
"closedOn":"Closed On",
"closedBy":"Closed By",
"claimedOn":"Claimed On",
"claimedBy":"Claimed By",
"pinnedOn":"Pinned On",
"pinnedBy":"Pinned By",
"deletedOn":"Deleted On",
"deletedBy":"Deleted By"
},
"roles":{
"developer":"Developer",
"serverOwner":"Server Owner",
"serverAdmin":"Server Admin",
"moderator":"Moderator Team",
"support":"Support Team",
"member":"Member"
}
},
"panel":{
"selectTicket":"Select your ticket",
"selectRole":"Select your role",
"selectOption":"Select your option"
},
"priorities":{
"urgent":"Urgent",
"veryHigh":"Very High",
"high":"High",
"normal":"Normal",
"low":"Low",
"veryLow":"Very Low",
"none":"None"
}
}
+145 -15
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["iamnotmega","ChatGPT"],
"lastedited":"21/10/2024",
"lastedited":"09/11/2025",
"language":"Estonian",
"automated":true
},
@@ -31,6 +31,14 @@
"stringContains":"See string peab sisaldama {0}!",
"stringChoices":"See string võib olla ainult üks järgmistest väärtustest: {0}!",
"stringRegex":"See string on kehtetu!",
"stringInvertedContains":"See string ei tohi sisaldada {0}!",
"stringLowercase":"See string peab olema kirjutatud ainult väikeste tähtedega!",
"stringUppercase":"See string peab olema kirjutatud ainult suurte tähtedega!",
"stringSpecialCharacters":"See string ei tohi sisaldada erimärke! (ainult a-z, 0-9 ja tühik)",
"stringNoSpaces":"See string ei tohi sisaldada tühikuid!",
"stringCapitalWord":"Soovitatav on, et iga sõna selles stringis algaks suure tähega!",
"stringCapitalSentence":"Tundub, et mõned laused selles stringis ei alga suure tähega!",
"stringPunctuation":"Tundub, et lause selles stringis ei lõpe kirjavahemärgiga!",
"numberTooShort":"See number ei tohi olla lühem kui {0} tähemärki!",
"numberTooLong":"See number ei tohi olla pikem kui {0} tähemärki!",
@@ -48,6 +56,8 @@
"numberNegative":"See arv ei saa olla negatiivne!",
"numberPositive":"See arv ei saa olla positiivne!",
"numberZero":"See arv ei saa olla null!",
"numberNan":"See number ei tohi olla NaN (mitte number)!",
"numberInvertedContains":"See number ei tohi sisaldada {0}!",
"booleanTrue":"See tõeväärtus ei saa olla tõsi!",
"booleanFalse":"See tõeväärtus ei saa olla vale!",
@@ -89,7 +99,8 @@
"invalidButton":"Sellel nupul peab olema vähemalt {0} või {1}!",
"unusedOption":"Valik {0} ei ole kusagil kasutusel!",
"unusedQuestion":"Küsimust {0} ei kasutata kuskil!",
"dropdownOption":"Lubatud rippmenüüga paneel võib sisaldada ainult \"pileti\" tüüpi valikuid!"
"dropdownOption":"Lubatud rippmenüüga paneel võib sisaldada ainult \"pileti\" tüüpi valikuid!",
"customInvalidVersion":"Teie konfiguratsioonis määratud versioon ei ühti! Veenduge, et olete konfiguratsiooni uuendanud uusimale versioonile!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Lisatud musta nimekirja",
"blacklistRemoveDm":"Eemaldatud mustast nimekirjast",
"clear":"Piletid kustutatud",
"clearTickets":"Kustuta Piletid",
"roles":"Rollid uuendatud",
"autoclose":"Pilet automaatselt suletud",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Automaatne sulgemine keelatud",
"autodelete":"Pilet on automaatselt kustutatud",
"autodeleteEnabled":"Automaatne kustutamine lubatud",
"autodeleteDisabled":"Automaatne kustutamine keelatud"
"autodeleteDisabled":"Automaatne kustutamine keelatud",
"topicSet":"Teema Muudetud",
"prioritySet":"Prioriteet Muudetud",
"priorityGet":"Pileti Prioriteet",
"transfer":"Pilet Üle Antud"
},
"descriptions":{
"create":"Teie pilet on loodud. Sellele juurdepääsuks klõpsake alloleval nupul!",
@@ -180,7 +197,12 @@
"ticketMessageLimit":"Saate korraga luua ainult {0} piletit!",
"ticketMessageAutoclose":"See pilet suletakse automaatselt, kui see on {0} tundi passiivne!",
"ticketMessageAutodelete":"See pilet kustutatakse automaatselt, kui see on {0} päeva passiivne!",
"panelReady":"Paneeli leiate altpoolt!\nSelle sõnumi saab nüüd kustutada!"
"panelReady":"Paneel on saadaval järelteates!\nSelle sõnumi võib nüüd kustutada!",
"topicSet":"Kanaliteema on edukalt muudetud kasutaja {0} poolt!",
"prioritySet":"Pileti prioriteet on edukalt muudetud väärtuseks {0} kasutaja {1} poolt!",
"priorityGet":"Selle pileti praegune prioriteet on {0}.",
"transfer":"Pileti omand on edukalt üle antud kasutajalt {0} kasutajale {1} kasutaja {2} poolt!"
},
"modal":{
"closePlaceholder":"Miks sa selle pileti kinni panid?",
@@ -220,7 +242,14 @@
"blacklistRemoveLog":"Kasutaja {1} eemaldas kasutaja {0} mustast nimekirjast!",
"blacklistAddDm":"Olete meie serveris mustas nimekirjas!\nNüüdsest ei saa te piletit luua!",
"blacklistRemoveDm":"Teid on meie serveri mustast nimekirjast eemaldatud!\nNüüd saate uuesti pileteid luua!",
"clearLog":"{1} on kustutanud {0} piletit!"
"clearLog":"{1} on kustutanud {0} piletit!",
"transferLog":"Selle pileti omand on üle antud kasutajalt {0} kasutajale {1} kasutaja {2} poolt!",
"transferDm":"Teie pileti omand on üle antud kasutajalt {0} kasutajale {1} meie serveris!",
"prioritySetLog":"Selle pileti prioriteet on muudetud väärtuseks {0} kasutaja {1} poolt!",
"prioritySetDm":"Teie pileti prioriteet on muudetud väärtuseks {0} meie serveris!",
"roleUpdateLog":"{0} uuendas oma rolle!",
"roleUpdateDm":"Teie rolle meie serveris on uuendatud!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"retry":"Proovi uuesti",
"continue":"Kustuta ilma ärakirjata",
"backup":"Loo varukoopia ärakirjast",
"error":"Midagi läks ärakirja loomisel valesti.\nMida sa teha tahaksid?\n\nSeda piletit ei kustutata enne, kui klõpsate ühel neist nuppudest."
"error":"Midagi läks ärakirja loomisel valesti.\nMida sa teha tahaksid?\n\nSeda piletit ei kustutata enne, kui klõpsate ühel neist nuppudest.",
"title":"Transkriptsiooniviga"
},
"text":{
"messagesTitle":"SÕNUMID",
"embedTitle":"EMBED",
"fileTitle":"FAIL",
"fieldsTitle":"VÄLJAD",
"reactionsTitle":"REAKTSIOONID",
"statsTitle":"STATISTIKA",
"emptyContent":"<sisu on tühi>",
"noTitle":"<pealkiri puudub>",
"noDesc":"<kirjeldus puudub>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Tundmatu paneel",
"notInGuild":"Pole serveris",
"channelRename":"Kanalit ei saa ümber nimetada",
"busy":"Pilet on kinni"
"busy":"Pilet on kinni",
"permissionError":"Õiguste Viga"
},
"descriptions":{
"askForInfo":"Lisateabe saamiseks võtke ühendust selle roboti omanikuga!",
@@ -280,7 +322,10 @@
"notInGuild":"See {0} ei tööta DM-is! Palun proovi uuesti serveris!",
"channelRename":"Ebakõlaliste kiiruspiirangute tõttu on robotil praegu võimatu kanalit ümber nimetada. Kui robotit ei taaskäivitata, nimetatakse kanal 10 minuti jooksul automaatselt ümber.",
"channelRenameSource":"Selle vea allikas on: {0}",
"busy":"Seda {0} ei saa kasutada!\nPiletit töötleb praegu bot.\n\nPalun proovige mõne sekundi pärast uuesti!"
"busy":"Seda {0} ei saa kasutada!\nPiletit töötleb praegu bot.\n\nPalun proovige mõne sekundi pärast uuesti!",
"closeBeforeMessage":"Seda piletit ei saa sulgeda/kustutada enne, kui kasutaja on sõnumi saatnud.",
"closeBeforeAdminMessage":"Seda piletit ei saa sulgeda/kustutada enne, kui piletihaldur või tugiliige on sõnumi saatnud.",
"unableToCreateTicket":"Te ei saa piletit luua."
},
"optionInvalidReasons":{
"stringRegex":"Väärtus ei vasta mustrile!",
@@ -331,7 +376,6 @@
"added":"Lisatud",
"removed":"Eemaldatud",
"filter":"Filter",
"claimedBy":"Nõude esitas {0}",
"method":"Meetod",
"type":"Tüüp",
"blacklisted":"Mustas nimekirjas",
@@ -355,7 +399,27 @@
"status":"Olek",
"claimed":"Nõutud",
"pinned":"Kinnitatud",
"creationDate":"Loomise kuupäev"
"creationDate":"Loomise kuupäev",
"noone":"Mitte Keegi",
"open":"Avatud",
"closed":"Suletud",
"priority":"Prioriteet",
"participants":"Osalejad",
"yes":"Jah",
"no":"Ei",
"option":"Valik",
"topic":"Teema",
"uptime":"Süsteemi Töötamise Aeg",
"messages":"Sõnumid",
"embeds":"Embedid",
"files":"Failid",
"components":"Komponendid",
"cooldown":"Jahtumisaeg",
"maxTickets":"Maksimaalselt Pileteid",
"admins":"Adminid",
"roles":"Rollid",
"size":"Suurus"
},
"lowercase":{
"text":"tekst",
@@ -384,6 +448,7 @@
"unclaim":"Tühista pilet.",
"pin":"Kinnitage pilet.",
"unpin":"Vabastage pilet.",
"move":"Liigutage pilet.",
"moveId":"Selle valiku identifikaator, millele soovite liikuda.",
"rename":"Nimetage pilet ümber.",
@@ -392,6 +457,7 @@
"addUser":"Lisatav kasutaja.",
"remove":"Kasutaja eemaldamine piletist.",
"removeUser":"Eemaldatav kasutaja.",
"blacklist":"Hallake piletite musta nimekirja.",
"blacklistView":"Vaadake praeguse musta nimekirja loendit.",
"blacklistAdd":"Lisage kasutaja musta nimekirja.",
@@ -405,6 +471,7 @@
"statsUserUser":"Kasutaja vaatamiseks.",
"statsTicket":"Vaadake serveris pileti statistikat.",
"statsTicketTicket":"Pilet vaatamiseks.",
"clear":"Kustutage mitu piletit korraga.",
"clearFilter":"Piletite puhastamise filter.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Vabastatud",
"autoclose":"Automaatselt suletud"
},
"autoclose":"Hallake piletis automaatset sulgemist.",
"autocloseDisable":"Keela sellel piletil automaatne sulgemine.",
"autocloseEnable":"Luba sellel piletil automaatne sulgemine.",
@@ -424,7 +492,19 @@
"autodelete":"Automaatse kustutamise haldamine piletis.",
"autodeleteDisable":"Keela sellel piletil automaatne kustutamine.",
"autodeleteEnable":"Luba sellel piletil automaatne kustutamine.",
"autodeleteEnableTime":"Päevade arv, mille jooksul see pilet peab olema selle kustutamiseks passiivne."
"autodeleteEnableTime":"Päevade arv, mille jooksul see pilet peab olema selle kustutamiseks passiivne.",
"topic":"Halda pileti kanali teemat.",
"topicSet":"Määra pileti kanali teema.",
"topicValue":"Kanali uus teema.",
"topicList":"Hangi nimekiri kõigist piletitest koos nende teemade ja statistikaga.",
"priority":"Halda pileti prioriteeti.",
"prioritySet":"Määra pileti prioriteet.",
"priorityValue":"Kanali prioriteet.",
"priorityGet":"Hangi pileti prioriteet.",
"priorityList":"Hangi nimekiri kõigist piletitest ja nende prioriteedi olekust.",
"transfer":"Anna pileti omand üle ühelt kasutajalt teisele.",
"transferUser":"Kasutaja, kellele omand üle anda."
},
"helpMenu":{
"help":"Hankige kõigi saadaolevate käskude loend.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Keela sellel piletil automaatne sulgemine.",
"autocloseEnable":"Luba sellel piletil automaatne sulgemine.",
"autodeleteDisable":"Keela sellel piletil automaatne kustutamine.",
"autodeleteEnable":"Luba sellel piletil automaatne kustutamine."
"autodeleteEnable":"Luba sellel piletil automaatne kustutamine.",
"categories":{
"general":"Üldised Käsud",
"basicTicket":"Põhilised Piletikäsud",
"advancedTicket":"Täpsemad Piletikäsud",
"userTicket":"Kasutaja Piletikäsud",
"admin":"Admini Käsud",
"advanced":"Täpsemad Käsud",
"extra":"Lisakäsud"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"Süsteemi statistika",
"user":"Kasutaja statistika",
"ticket":"Piletistatistika",
"participants":"Osalejad"
"participants":"Osalejad",
"messages":"Sõnumid"
},
"properties":{
"ticketsCreated":"Pileteid loodud",
@@ -472,7 +562,47 @@
"ticketsPinned":"Pileteid kinnitatud",
"ticketsMoved":"Pileteid teisaldatud",
"usersBlacklisted":"Kasutajaid mustas nimekirjas",
"transcriptsCreated":"Loodud ärakirjad"
"transcriptsCreated":"Loodud ärakirjad",
"ticketsAutodeleted":"Piletid Automaatselt Kustutatud",
"ticketsTransferred":"Piletid Üle Antud",
"ticketVolume":"Piletite Arv",
"averageTickets":"Keskmine Piletite Arv/Kasutaja",
"currentTickets":"Praegused Piletid",
"age":"Pileti Vanus",
"responseTime":"Reageerimisaeg",
"resolutionTime":"Lahendusaeg",
"createdOn":"Loodud",
"createdBy":"Looja",
"closedOn":"Suletud",
"closedBy":"Sulgenud",
"claimedOn":"Võetud",
"claimedBy":"Võtnud",
"pinnedOn":"Kinnitatud",
"pinnedBy":"Kinnitanud",
"deletedOn":"Kustutatud",
"deletedBy":"Kustutanud"
},
"roles":{
"developer":"Arendaja",
"serverOwner":"Serveri Omanik",
"serverAdmin":"Serveri Administraator",
"moderator":"Moderaatorite Meeskond",
"support":"Tugimeeskond",
"member":"Liige"
}
},
"panel":{
"selectTicket":"Vali oma pilet",
"selectRole":"Vali oma roll",
"selectOption":"Vali oma valik"
},
"priorities":{
"urgent":"Kiireloomuline",
"veryHigh":"Väga Kõrge",
"high":"Kõrge",
"normal":"Tavaline",
"low":"Madal",
"veryLow":"Väga Madal",
"none":"Puudub"
}
}
+146 -16
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["iamnotmega","ChatGPT"],
"lastedited":"21/10/2024",
"lastedited":"09/11/2025",
"language":"Finnish",
"automated":true
},
@@ -31,6 +31,14 @@
"stringContains":"Tämän merkkijonon täytyy sisältää {0}!",
"stringChoices":"Tämä merkkijono voi olla vain yksi seuraavista arvoista: {0}!",
"stringRegex":"Tämä merkkijono on virheellinen!",
"stringInvertedContains":"Tämä merkkijono ei saa sisältää {0}!",
"stringLowercase":"Tämä merkkijono on kirjoitettava vain pienillä kirjaimilla!",
"stringUppercase":"Tämä merkkijono on kirjoitettava vain isoilla kirjaimilla!",
"stringSpecialCharacters":"Tämä merkkijono ei saa sisältää erikoismerkkejä! (vain a–z, 0–9 ja välilyönti)",
"stringNoSpaces":"Tämä merkkijono ei saa sisältää välilyöntejä!",
"stringCapitalWord":"On suositeltavaa, että jokainen sana tässä merkkijonossa alkaa isolla kirjaimella!",
"stringCapitalSentence":"Näyttää siltä, että jotkut lauseet tässä merkkijonossa eivät ala isolla kirjaimella!",
"stringPunctuation":"Näyttää siltä, että tämän merkkijonon lause ei pääty välimerkkiin!",
"numberTooShort":"Tämä numero ei voi olla lyhyempi kuin {0} merkkiä!",
"numberTooLong":"Tämä numero ei voi olla pidempi kuin {0} merkkiä!",
@@ -48,6 +56,8 @@
"numberNegative":"Tämä luku ei voi olla negatiivinen!",
"numberPositive":"Tämä luku ei voi olla positiivinen!",
"numberZero":"Tämä luku ei voi olla nolla!",
"numberNan":"Tämä numero ei voi olla NaN (ei numero)!",
"numberInvertedContains":"Tämä numero ei saa sisältää {0}!",
"booleanTrue":"Tämä boolean ei voi olla totta!",
"booleanFalse":"Tämä boolean ei voi olla väärä!",
@@ -89,7 +99,8 @@
"invalidButton":"Tässä painikkeessa on oltava vähintään {0} tai {1}!",
"unusedOption":"Vaihtoehtoa {0} ei käytetä missään!",
"unusedQuestion":"Kysymystä {0} ei käytetä missään!",
"dropdownOption":"Paneeli, jossa pudotusvalikko on käytössä, voi sisältää vain \"lippu\"-tyyppisiä vaihtoehtoja!"
"dropdownOption":"Paneeli, jossa pudotusvalikko on käytössä, voi sisältää vain \"lippu\"-tyyppisiä vaihtoehtoja!",
"customInvalidVersion":"Määritetty versio kokoonpanossasi ei vastaa! Varmista, että olet päivittänyt kokoonpanon uusimpaan versioon!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Lisätty mustalle listalle",
"blacklistRemoveDm":"Poistettu mustalta listalta",
"clear":"Liput tyhjennetty",
"clearTickets":"Tyhjennä Tiketit",
"roles":"Roolit päivitetty",
"autoclose":"Lippu suljettu automaattisesti",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Automaattinen sulkeminen pois käytöstä",
"autodelete":"Lippu poistettu automaattisesti",
"autodeleteEnabled":"Automaattinen poisto käytössä",
"autodeleteDisabled":"Automaattinen poisto poissa käytöstä"
"autodeleteDisabled":"Automaattinen poisto poissa käytöstä",
"topicSet":"Aihe Muutettu",
"prioritySet":"Prioriteetti Muutettu",
"priorityGet":"Tiketin Prioriteetti",
"transfer":"Tiketti Siirretty"
},
"descriptions":{
"create":"Lippusi on luotu. Napsauta alla olevaa painiketta päästäksesi siihen!",
@@ -180,7 +197,12 @@
"ticketMessageLimit":"Voit luoda vain {0} lippua samanaikaisesti!",
"ticketMessageAutoclose":"Tämä lippu suljetaan automaattisesti, kun se ei ole aktiivinen {0} tuntia!",
"ticketMessageAutodelete":"Tämä lippu poistetaan automaattisesti, kun se ei ole aktiivinen {0} päivään!",
"panelReady":"Löydät paneelin alta! Tämä viesti voidaan nyt poistaa!"
"panelReady":"Paneeli on saatavilla jatkoviestissä!\nTämä viesti voidaan nyt poistaa!",
"topicSet":"Kanavan aihe on vaihdettu käyttäjän {0} toimesta onnistuneesti!",
"prioritySet":"Tiketin prioriteetti on vaihdettu arvoksi {0} käyttäjän {1} toimesta onnistuneesti!",
"priorityGet":"Tämän tiketin nykyinen prioriteetti on {0}.",
"transfer":"Tiketin omistus on siirretty käyttäjältä {0} käyttäjälle {1} käyttäjän {2} toimesta onnistuneesti!"
},
"modal":{
"closePlaceholder":"Miksi suljit tämän lipun?",
@@ -220,7 +242,14 @@
"blacklistRemoveLog":"{1} poisti käyttäjän {0} mustalta listalta!",
"blacklistAddDm":"Sinut on mustalla listalla palvelimellamme! Tästä eteenpäin et voi luoda lippua!",
"blacklistRemoveDm":"Sinut on poistettu palvelimemme mustalta listalta! Nyt voit luoda lippuja uudelleen!",
"clearLog":"{1} on poistanut {0} lippua!"
"clearLog":"{1} on poistanut {0} lippua!",
"transferLog":"Tämän tiketin omistus on siirretty käyttäjältä {0} käyttäjälle {1} käyttäjän {2} toimesta!",
"transferDm":"Tiketin omistus on siirretty käyttäjältä {0} käyttäjälle {1} palvelimellamme!",
"prioritySetLog":"Tämän tiketin prioriteetti on vaihdettu arvoksi {0} käyttäjän {1} toimesta!",
"prioritySetDm":"Tiketin prioriteetti on vaihdettu arvoksi {0} palvelimellamme!",
"roleUpdateLog":"{0} on päivittänyt roolinsa!",
"roleUpdateDm":"Roolisi palvelimellamme on päivitetty!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"retry":"Yritä uudelleen",
"continue":"Poista ilman transkriptiota",
"backup":"Luo varmuuskopio",
"error":"Jotain meni pieleen, kun yritettiin luoda transkriptiota. Mitä haluaisit tehdä? Tätä lippua ei poisteta, ennen kuin napsautat jotakin näistä painikkeista. "
"error":"Jotain meni pieleen, kun yritettiin luoda transkriptiota. Mitä haluaisit tehdä? Tätä lippua ei poisteta, ennen kuin napsautat jotakin näistä painikkeista. ",
"title":"Transkriptiovirhe"
},
"text":{
"messagesTitle":"VIESTIT",
"embedTitle":"UPOTUS",
"fileTitle":"TIEDOSTO",
"fieldsTitle":"KENTÄT",
"reactionsTitle":"REAKTIOT",
"statsTitle":"TILASTOT",
"emptyContent":"<sisältö on tyhjä>",
"noTitle":"<ei-otsikkoa>",
"noDesc":"<ei-kuvausta>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Tuntematon paneeli",
"notInGuild":"Ei Palvelimessa",
"channelRename":"Kanavaa ei voi nimetä uudelleen",
"busy":"Lippu on varattu"
"busy":"Lippu on varattu",
"permissionError":"Käyttöoikeusvirhe"
},
"descriptions":{
"askForInfo":"Ota yhteyttä tämän botin omistajaan saadaksesi lisätietoja!",
@@ -280,7 +322,10 @@
"notInGuild":"Tämä {0} ei toimi DM:ssä! Ole hyvä ja yritä uudelleen palvelimella!",
"channelRename":"Discordin nopeusrajoitusten vuoksi botin on tällä hetkellä mahdotonta nimetä kanavaa uudelleen. Kanava nimetään automaattisesti uudelleen 10 minuutin kuluttua, jos bottia ei käynnistetä uudelleen.",
"channelRenameSource":"Tämän virheen lähde on: {0}",
"busy":"Tätä {0} ei voi käyttää! Botti käsittelee lippua parhaillaan. Yritä uudelleen muutaman sekunnin kuluttua!"
"busy":"Tätä {0} ei voi käyttää! Botti käsittelee lippua parhaillaan. Yritä uudelleen muutaman sekunnin kuluttua!",
"closeBeforeMessage":"Tätä tikettiä ei voi sulkea/poistaa ennen kuin käyttäjä on lähettänyt viestin.",
"closeBeforeAdminMessage":"Tätä tikettiä ei voi sulkea/poistaa ennen kuin tikettien ylläpitäjä tai tukihenkilö on lähettänyt viestin.",
"unableToCreateTicket":"Et voi luoda tikettiä."
},
"optionInvalidReasons":{
"stringRegex":"Arvo ei vastaa mallia!",
@@ -331,7 +376,6 @@
"added":"Lisätty",
"removed":"Poistettu",
"filter":"Suodattaa",
"claimedBy":"Vaatimuksen tekijä: {0}",
"method":"Menetelmä",
"type":"Tyyppi",
"blacklisted":"Mustalla listalla",
@@ -352,10 +396,30 @@
"version":"Versio",
"name":"Nimi",
"role":"Rooli",
"status":"Status",
"status":"Tila",
"claimed":"Väitetty",
"pinned":"Kiinnitetty",
"creationDate":"Luontipäivämäärä"
"creationDate":"Luontipäivämäärä",
"noone":"Ei Kukaan",
"open":"Avoin",
"closed":"Suljettu",
"priority":"Prioriteetti",
"participants":"Osallistujat",
"yes":"Kyllä",
"no":"Ei",
"option":"Vaihtoehto",
"topic":"Aihe",
"uptime":"Järjestelmän Käyttöaika",
"messages":"Viestit",
"embeds":"Upotukset",
"files":"Tiedostot",
"components":"Komponentit",
"cooldown":"Jäähy",
"maxTickets":"Maksimi Tiketit",
"admins":"Ylläpitäjät",
"roles":"Roolit",
"size":"Koko"
},
"lowercase":{
"text":"teksti",
@@ -384,6 +448,7 @@
"unclaim":"Peruuta lippu.",
"pin":"Kiinnitä lippu.",
"unpin":"Irrota lippu.",
"move":"Siirrä lippu.",
"moveId":"Sen vaihtoehdon tunniste, johon haluat siirtyä.",
"rename":"Nimeä lippu uudelleen.",
@@ -392,6 +457,7 @@
"addUser":"Lisättävä käyttäjä.",
"remove":"Poista käyttäjä lipusta.",
"removeUser":"Poistettava käyttäjä.",
"blacklist":"Hallitse lippujen mustaa listaa.",
"blacklistView":"Näytä luettelo nykyisestä mustasta listasta.",
"blacklistAdd":"Lisää käyttäjä mustalle listalle.",
@@ -405,6 +471,7 @@
"statsUserUser":"Käyttäjä katsella.",
"statsTicket":"Tarkastele lipun tilastoja palvelimella.",
"statsTicketTicket":"Lippu katsottavaksi.",
"clear":"Poista useita lippuja samanaikaisesti.",
"clearFilter":"Lippujen tyhjennyssuodatin.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Irrotettu",
"autoclose":"Automaattisuljettu"
},
"autoclose":"Hallitse automaattista sulkemista lipussa.",
"autocloseDisable":"Poista automaattinen sulkeminen käytöstä tässä lipussa.",
"autocloseEnable":"Ota automaattinen sulkeminen käyttöön tässä lipussa.",
@@ -424,7 +492,19 @@
"autodelete":"Hallitse automaattista poistoa lipussa.",
"autodeleteDisable":"Poista automaattinen poisto käytöstä tässä lipussa.",
"autodeleteEnable":"Ota automaattinen poisto käyttöön tässä lipussa.",
"autodeleteEnableTime":"Kuinka monta päivää tämän lipun on oltava passiivinen, jotta se voidaan poistaa."
"autodeleteEnableTime":"Kuinka monta päivää tämän lipun on oltava passiivinen, jotta se voidaan poistaa.",
"topic":"Hallitse tikettikanavan aihetta.",
"topicSet":"Aseta tikettikanavan aihe.",
"topicValue":"Kanavan uusi aihe.",
"topicList":"Hae lista kaikista tiketeistä niiden aiheen ja tilastojen kanssa.",
"priority":"Hallitse tiketin prioriteettia.",
"prioritySet":"Aseta tiketin prioriteetti.",
"priorityValue":"Kanavan prioriteetti.",
"priorityGet":"Hae tiketin prioriteetti.",
"priorityList":"Hae lista kaikista tiketeistä ja niiden prioriteettitilasta.",
"transfer":"Siirrä tiketin omistus käyttäjältä toiselle.",
"transferUser":"Käyttäjä, jolle omistus siirretään."
},
"helpMenu":{
"help":"Hanki luettelo kaikista käytettävissä olevista komennoista.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Poista automaattinen sulkeminen käytöstä tässä lipussa.",
"autocloseEnable":"Ota automaattinen sulkeminen käyttöön tässä lipussa.",
"autodeleteDisable":"Poista automaattinen poisto käytöstä tässä lipussa.",
"autodeleteEnable":"Ota automaattinen poisto käyttöön tässä lipussa."
"autodeleteEnable":"Ota automaattinen poisto käyttöön tässä lipussa.",
"categories":{
"general":"Yleiset Komennot",
"basicTicket":"Perus Tikettikomennot",
"advancedTicket":"Edistyneet Tikettikomennot",
"userTicket":"Käyttäjän Tikettikomennot",
"admin":"Ylläpitäjäkomennot",
"advanced":"Edistyneet Komennot",
"extra":"Lisäkomennot"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"Järjestelmätilastot",
"user":"Käyttäjätilastot",
"ticket":"Lipputilastot",
"participants":"Osallistujat"
"participants":"Osallistujat",
"messages":"Viestit"
},
"properties":{
"ticketsCreated":"Liput luotu",
@@ -472,7 +562,47 @@
"ticketsPinned":"Liput kiinni",
"ticketsMoved":"Liput siirretty",
"usersBlacklisted":"Käyttäjät mustalla listalla",
"transcriptsCreated":"Transkriptiot luotu"
"transcriptsCreated":"Transkriptiot luotu",
"ticketsAutodeleted":"Tiketit Automaattisesti Poistettu",
"ticketsTransferred":"Tiketit Siirretty",
"ticketVolume":"Tikettilukumäärä",
"averageTickets":"Keskimääräiset Tiketit/Käyttäjä",
"currentTickets":"Nykyiset Tiketit",
"age":"Tiketin Ikä",
"responseTime":"Vastausaika",
"resolutionTime":"Ratkaisuaika",
"createdOn":"Luotu",
"createdBy":"Luonut",
"closedOn":"Suljettu",
"closedBy":"Sulkenut",
"claimedOn":"Otettu Vastaan",
"claimedBy":"Vastaanottanut",
"pinnedOn":"Kiinnitetty",
"pinnedBy":"Kiinnittänyt",
"deletedOn":"Poistettu",
"deletedBy":"Poistanut"
},
"roles":{
"developer":"Kehittäjä",
"serverOwner":"Palvelimen Omistaja",
"serverAdmin":"Palvelimen Ylläpitäjä",
"moderator":"Moderaattoritiimi",
"support":"Tukitiimi",
"member":"Jäsen"
}
},
"panel":{
"selectTicket":"Valitse tikettisi",
"selectRole":"Valitse roolisi",
"selectOption":"Valitse vaihtoehtosi"
},
"priorities":{
"urgent":"Kiireellinen",
"veryHigh":"Erittäin Korkea",
"high":"Korkea",
"normal":"Normaali",
"low":"Matala",
"veryLow":"Erittäin Matala",
"none":"Ei Mikään"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["guillee3"],
"lastedited":"04/12/2024",
"language":"French",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["benzorich"],
"lastedited":"06/10/2024",
"language":"German",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Greek",
+146 -16
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"translators":["an_developer"],
"lastedited":"14/12/2024",
"otversion":"v4.1.0",
"translators":["challenger_nova"],
"lastedited":"09/11/2025",
"language":"Hindi",
"automated":false
},
@@ -31,6 +31,14 @@
"stringContains":"इस स्ट्रिंग में {0} होना आवश्यक है!",
"stringChoices":"यह स्ट्रिंग केवल निम्नलिखित मानों में से एक हो सकती है: {0}!",
"stringRegex":"यह स्ट्रिंग अमान्य है!",
"stringInvertedContains":"इस स्ट्रिंग में {0} शामिल नहीं होना चाहिए!",
"stringLowercase":"यह स्ट्रिंग केवल छोटे अक्षरों में लिखी जानी चाहिए!",
"stringUppercase":"यह स्ट्रिंग केवल बड़े अक्षरों में लिखी जानी चाहिए!",
"stringSpecialCharacters":"इस स्ट्रिंग में कोई विशेष अक्षर नहीं होना चाहिए! (केवल a-z, 0-9 और स्पेस अनुमत हैं)",
"stringNoSpaces":"इस स्ट्रिंग में स्पेस नहीं होना चाहिए!",
"stringCapitalWord":"यह अनुशंसा की जाती है कि इस स्ट्रिंग के प्रत्येक शब्द की शुरुआत बड़े अक्षर से हो!",
"stringCapitalSentence":"ऐसा लगता है कि इस स्ट्रिंग में कुछ वाक्य बड़े अक्षर से शुरू नहीं होते हैं!",
"stringPunctuation":"ऐसा लगता है कि इस स्ट्रिंग का वाक्य विराम चिह्न के साथ समाप्त नहीं होता है!",
"numberTooShort":"यह संख्या {0} वर्णों से छोटी नहीं हो सकती!",
"numberTooLong":"यह संख्या {0} वर्णों से अधिक लंबी नहीं हो सकती!",
@@ -48,6 +56,8 @@
"numberNegative":"यह संख्या ऋणात्मक नहीं हो सकती!",
"numberPositive":"यह संख्या सकारात्मक नहीं हो सकती!",
"numberZero":"यह संख्या शून्य नहीं हो सकती!",
"numberNan":"यह संख्या NaN (Not A Number) नहीं हो सकती!",
"numberInvertedContains":"इस संख्या में {0} शामिल नहीं होना चाहिए!",
"booleanTrue":"यह बूलियन सत्य नहीं हो सकता!",
"booleanFalse":"यह बूलियन झूठा नहीं हो सकता!",
@@ -89,7 +99,8 @@
"invalidButton":"इस बटन पर कम से कम {0} या {1} होना आवश्यक है!",
"unusedOption":"विकल्प {0} का उपयोग कहीं भी नहीं किया जाता है!",
"unusedQuestion":"प्रश्न {0} का प्रयोग कहीं भी नहीं किया गया है!",
"dropdownOption":"ड्रॉपडाउन सक्षम पैनल में केवल 'टिकट' प्रकार के विकल्प हो सकते हैं!"
"dropdownOption":"ड्रॉपडाउन सक्षम पैनल में केवल 'टिकट' प्रकार के विकल्प हो सकते हैं!",
"customInvalidVersion":"आपके कॉन्फ़िग में निर्दिष्ट संस्करण मेल नहीं खाता! कृपया सुनिश्चित करें कि आपने कॉन्फ़िग को नवीनतम संस्करण में अपडेट किया है!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"ब्लैकलिस्ट में जोड़ा गया",
"blacklistRemoveDm":"काली सूची से हटाया गया",
"clear":"टिकट साफ़ हो गए",
"clearTickets":"टिकट साफ़ करें",
"roles":"भूमिकाएँ अद्यतन की गईं",
"autoclose":"टिकट स्वतः बंद",
@@ -139,7 +151,12 @@
"autocloseDisabled":"स्वत: बंद अक्षम",
"autodelete":"टिकट स्वतः हटा दिया गया",
"autodeleteEnabled":"स्वतः हटाना सक्षम",
"autodeleteDisabled":"स्वतः हटाना अक्षम"
"autodeleteDisabled":"स्वतः हटाना अक्षम",
"topicSet":"विषय बदला गया",
"prioritySet":"प्राथमिकता बदली गई",
"priorityGet":"टिकट प्राथमिकता",
"transfer":"टिकट स्थानांतरित किया गया"
},
"descriptions":{
"create":"आपका टिकट बन गया है. ",
@@ -180,7 +197,12 @@
"ticketMessageLimit":"आप एक ही समय में केवल {0} टिकट बना सकते हैं!",
"ticketMessageAutoclose":"{0}घंटे तक निष्क्रिय रहने पर यह टिकट स्वतः बंद हो जाएगा!",
"ticketMessageAutodelete":"{0} दिनों तक निष्क्रिय रहने पर यह टिकट स्वतः हटा दिया जाएगा!",
"panelReady":"आप नीचे पैनल पा सकते हैं!\n"
"panelReady":"पैनल फॉलोअप संदेश में उपलब्ध है!\nयह संदेश अब हटाया जा सकता है!",
"topicSet":"चैनल का विषय {0} द्वारा सफलतापूर्वक बदल दिया गया है!",
"prioritySet":"टिकट की प्राथमिकता {1} द्वारा {0} में सफलतापूर्वक बदल दी गई है!",
"priorityGet":"इस टिकट की वर्तमान प्राथमिकता {0} है।",
"transfer":"टिकट का स्वामित्व {0} से {1} में {2} द्वारा सफलतापूर्वक स्थानांतरित कर दिया गया है!"
},
"modal":{
"closePlaceholder":"आपने यह टिकट क्यों बंद कर दिया?",
@@ -220,7 +242,14 @@
"blacklistRemoveLog":"{0} को {1} द्वारा काली सूची से हटा दिया गया था!",
"blacklistAddDm":"आपको हमारे सर्वर में काली सूची में डाल दिया गया है!\n",
"blacklistRemoveDm":"आपको हमारे सर्वर में ब्लैकलिस्ट से हटा दिया गया है!\n",
"clearLog":"{0} टिकटें {1} द्वारा हटा दी गई हैं!"
"clearLog":"{0} टिकटें {1} द्वारा हटा दी गई हैं!",
"transferLog":"इस टिकट का स्वामित्व {0} से {1} में {2} द्वारा स्थानांतरित किया गया है!",
"transferDm":"आपके टिकट का स्वामित्व हमारे सर्वर में {0} से {1} में स्थानांतरित कर दिया गया है!",
"prioritySetLog":"इस टिकट की प्राथमिकता {1} द्वारा {0} में बदल दी गई है!",
"prioritySetDm":"आपके टिकट की प्राथमिकता हमारे सर्वर में {0} में बदल दी गई है!",
"roleUpdateLog":"{0} ने अपनी भूमिकाएँ अपडेट की हैं!",
"roleUpdateDm":"आपकी भूमिकाएँ हमारे सर्वर में अपडेट कर दी गई हैं!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"retry":"पुन: प्रयास करें",
"continue":"प्रतिलेख के बिना हटाएँ",
"backup":"बैकअप ट्रांस्क्रिप्ट बनाएं",
"error":"प्रतिलेख बनाने का प्रयास करते समय कुछ गलत हो गया।\n"
"error":"प्रतिलेख बनाने का प्रयास करते समय कुछ गलत हो गया।\n",
"title":"ट्रांसक्रिप्ट त्रुटि"
},
"text":{
"messagesTitle":"संदेश",
"embedTitle":"एम्बेड",
"fileTitle":"फ़ाइल",
"fieldsTitle":"फ़ील्ड्स",
"reactionsTitle":"प्रतिक्रियाएँ",
"statsTitle":"आँकड़े",
"emptyContent":"<सामग्री खाली है>",
"noTitle":"<कोई शीर्षक नहीं>",
"noDesc":"<कोई विवरण नहीं>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"अज्ञात पैनल",
"notInGuild":"सर्वर में नहीं",
"channelRename":"चैनल का नाम बदलने में असमर्थ",
"busy":"टिकट व्यस्त है"
"busy":"टिकट व्यस्त है",
"permissionError":"अनुमति त्रुटि"
},
"descriptions":{
"askForInfo":"अधिक जानकारी के लिए इस बॉट के मालिक से संपर्क करें!",
@@ -280,7 +322,10 @@
"notInGuild":"यह {0} डीएम में काम नहीं करता! ",
"channelRename":"विवाद की दर सीमा के कारण, वर्तमान में बॉट के लिए चैनल का नाम बदलना असंभव है। ",
"channelRenameSource":"इस त्रुटि का स्रोत है: {0}",
"busy":"इस {0} का उपयोग करने में असमर्थ!\n"
"busy":"इस {0} का उपयोग करने में असमर्थ!\n",
"closeBeforeMessage":"किसी उपयोगकर्ता द्वारा संदेश भेजे जाने से पहले इस टिकट को बंद/हटाया नहीं जा सकता।",
"closeBeforeAdminMessage":"टिकट एडमिन या सपोर्ट सदस्य द्वारा संदेश भेजे जाने से पहले इस टिकट को बंद/हटाया नहीं जा सकता।",
"unableToCreateTicket":"आप टिकट बनाने में असमर्थ हैं।"
},
"optionInvalidReasons":{
"stringRegex":"मान पैटर्न से मेल नहीं खाता!",
@@ -331,7 +376,6 @@
"added":"जोड़ा",
"removed":"निकाला गया",
"filter":"फ़िल्टर",
"claimedBy":"{0} द्वारा दावा किया गया",
"method":"तरीका",
"type":"प्रकार",
"blacklisted":"ब्लैक लिस्ट किया",
@@ -355,7 +399,27 @@
"status":"स्थिति",
"claimed":"दावा किया",
"pinned":"पिन की गई",
"creationDate":"निर्माण तिथि"
"creationDate":"निर्माण तिथि",
"noone":"कोई नहीं",
"open":"खुला",
"closed":"बंद",
"priority":"प्राथमिकता",
"participants":"प्रतिभागी",
"yes":"हाँ",
"no":"नहीं",
"option":"विकल्प",
"topic":"विषय",
"uptime":"सिस्टम अपटाइम",
"messages":"संदेश",
"embeds":"एम्बेड्स",
"files":"फ़ाइलें",
"components":"घटक",
"cooldown":"कूलडाउन",
"maxTickets":"अधिकतम टिकट",
"admins":"एडमिन",
"roles":"भूमिकाएँ",
"size":"आकार"
},
"lowercase":{
"text":"मूलपाठ",
@@ -384,6 +448,7 @@
"unclaim":"टिकट का दावा रद्द करें.",
"pin":"टिकट पिन करें.",
"unpin":"टिकट अनपिन करें.",
"move":"एक टिकट ले जाएँ.",
"moveId":"उस विकल्प का पहचानकर्ता जिस पर आप जाना चाहते हैं.",
"rename":"टिकट का नाम बदलें.",
@@ -392,6 +457,7 @@
"addUser":"उपयोगकर्ता को जोड़ना है.",
"remove":"किसी उपयोगकर्ता को टिकट से हटाएँ.",
"removeUser":"उपयोगकर्ता को हटाना है.",
"blacklist":"टिकट ब्लैकलिस्ट प्रबंधित करें.",
"blacklistView":"वर्तमान ब्लैकलिस्ट की सूची देखें.",
"blacklistAdd":"किसी उपयोगकर्ता को काली सूची में जोड़ें.",
@@ -405,6 +471,7 @@
"statsUserUser":"देखने के लिए उपयोगकर्ता.",
"statsTicket":"सर्वर में टिकट के आँकड़े देखें।",
"statsTicketTicket":"देखने के लिए टिकट.",
"clear":"एक ही समय में एकाधिक टिकट हटाएँ।",
"clearFilter":"टिकट साफ़ करने के लिए फ़िल्टर.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"अनपिन किया गया",
"autoclose":"स्वतः बंद"
},
"autoclose":"किसी टिकट में ऑटोक्लोज़ प्रबंधित करें.",
"autocloseDisable":"इस टिकट में ऑटोक्लोज़ अक्षम करें.",
"autocloseEnable":"इस टिकट में ऑटोक्लोज़ सक्षम करें।",
@@ -424,7 +492,19 @@
"autodelete":"किसी टिकट में स्वत: हटाना प्रबंधित करें.",
"autodeleteDisable":"इस टिकट में ऑटोडिलीट अक्षम करें।",
"autodeleteEnable":"इस टिकट में ऑटोडिलीट सक्षम करें।",
"autodeleteEnableTime":"इस टिकट को हटाने के लिए उतने दिनों की संख्या निष्क्रिय होनी चाहिए।"
"autodeleteEnableTime":"इस टिकट को हटाने के लिए उतने दिनों की संख्या निष्क्रिय होनी चाहिए।",
"topic":"टिकट चैनल के विषय का प्रबंधन करें।",
"topicSet":"टिकट चैनल का विषय सेट करें।",
"topicValue":"चैनल का नया विषय।",
"topicList":"सभी टिकटों की सूची प्राप्त करें जिनके विषय और आँकड़े शामिल हैं।",
"priority":"टिकट की प्राथमिकता का प्रबंधन करें।",
"prioritySet":"टिकट की प्राथमिकता सेट करें।",
"priorityValue":"चैनल की प्राथमिकता।",
"priorityGet":"टिकट की प्राथमिकता प्राप्त करें।",
"priorityList":"सभी टिकटों की प्राथमिकता स्थिति की सूची प्राप्त करें।",
"transfer":"टिकट का स्वामित्व एक उपयोगकर्ता से दूसरे को स्थानांतरित करें।",
"transferUser":"जिस उपयोगकर्ता को स्थानांतरित करना है।"
},
"helpMenu":{
"help":"सभी उपलब्ध आदेशों की सूची प्राप्त करें.",
@@ -452,7 +532,16 @@
"autocloseDisable":"इस टिकट में ऑटोक्लोज़ अक्षम करें.",
"autocloseEnable":"इस टिकट में ऑटोक्लोज़ सक्षम करें।",
"autodeleteDisable":"इस टिकट में ऑटोडिलीट अक्षम करें।",
"autodeleteEnable":"इस टिकट में ऑटोडिलीट सक्षम करें।"
"autodeleteEnable":"इस टिकट में ऑटोडिलीट सक्षम करें।",
"categories":{
"general":"सामान्य कमांड्स",
"basicTicket":"मूल टिकट कमांड्स",
"advancedTicket":"उन्नत टिकट कमांड्स",
"userTicket":"उपयोगकर्ता टिकट कमांड्स",
"admin":"एडमिन कमांड्स",
"advanced":"उन्नत कमांड्स",
"extra":"अतिरिक्त कमांड्स"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"सिस्टम आँकड़े",
"user":"उपयोगकर्ता आँकड़े",
"ticket":"टिकट आँकड़े",
"participants":"प्रतिभागियों"
"participants":"प्रतिभागियों",
"messages":"संदेश"
},
"properties":{
"ticketsCreated":"टिकट बनाये गये",
@@ -472,7 +562,47 @@
"ticketsPinned":"टिकट पिन किये गये",
"ticketsMoved":"टिकट ले जाया गया",
"usersBlacklisted":"उपयोगकर्ता ब्लैकलिस्टेड",
"transcriptsCreated":"प्रतिलिपियाँ बनाई गईं"
"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.0.7",
"otversion":"v4.1.0",
"translators":["Kornel0706"],
"lastedited":"22/08/2024",
"language":"Hungarian",
+147 -17
View File
@@ -1,8 +1,8 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["erxg"],
"lastedited":"25/08/2024",
"lastedited":"09/11/2025",
"language":"Indonesian",
"automated":false
},
@@ -31,6 +31,14 @@
"stringContains":"String ini harus mengandung {0}!",
"stringChoices":"String ini hanya dapat berupa salah satu dari nilai berikut ini: {0}!",
"stringRegex":"String ini tidak valid!",
"stringInvertedContains":"Teks ini tidak boleh mengandung {0}!",
"stringLowercase":"Teks ini harus ditulis dalam huruf kecil saja!",
"stringUppercase":"Teks ini harus ditulis dalam huruf kecil besar!",
"stringSpecialCharacters":"Teks ini tidak boleh mengandung karakter khusus apa pun! (Hanya huruf a-z, angka 0-9, dan spasi)",
"stringNoSpaces":"Teks ini tidak boleh mengandung spasi",
"stringCapitalWord":"Disarankan agar setiap kata dalam teks ini dimulai dengan huruf kapital!",
"stringCapitalSentence":"Sepertinya beberapa kalimat dalam teks ini tidak dimulai dengan huruf kapital!",
"stringPunctuation":"Sepertinya kalimat dalam teks ini tidak diakhiri dengan tanda baca!",
"numberTooShort":"Nomor ini tidak boleh lebih pendek dari {0} karakter!",
"numberTooLong":"Nomor ini tidak boleh lebih panjang dari {0} karakter!",
@@ -48,6 +56,8 @@
"numberNegative":"Nomor ini tidak boleh negatif!",
"numberPositive":"Nomor ini tidak boleh positif!",
"numberZero":"Nomor ini tidak boleh nol!",
"numberNan":"Nomor ini tidak boleh NaN (Bukan Angka)!",
"numberInvertedContains":"Nomor ini tidak boleh mengandung {0}!",
"booleanTrue":"Boolean ini tidak boleh benar!",
"booleanFalse":"Boolean ini tidak boleh salah!",
@@ -89,7 +99,8 @@
"invalidButton":"Tombol ini harus memiliki setidaknya {0} atau {1}!",
"unusedOption":"Opsi {0} tidak digunakan di mana pun!",
"unusedQuestion":"Pertanyaan {0} tidak digunakan di mana pun!",
"dropdownOption":"Panel dengan menu tarik-ulur yang diaktifkan hanya dapat berisi opsi dengan tipe 'tiket'!"
"dropdownOption":"Panel dengan menu tarik-ulur yang diaktifkan hanya dapat berisi opsi dengan tipe 'tiket'!",
"customInvalidVersion":"Versi yang ditetapkan dalam konfigurasi tidak sesuai! Pastikan konfigurasi Anda telah diperbarui ke versi terbaru!"
}
},
"actions":{
@@ -132,6 +143,7 @@
"blacklistAddDm":"Ditambahkan ke Daftar Hitam",
"blacklistRemoveDm":"Dihapus dari Daftar Hitam",
"clear":"Tiket dibersihkan.",
"clearTickets":"Bersihkan Tiket",
"roles":"Peran Diperbarui",
"autoclose":"Tiket Ditutup Otomatis",
@@ -139,7 +151,12 @@
"autocloseDisabled":"Tutup Otomatis Dimatikan",
"autodelete":"Tiket Dihapus Otomatis",
"autodeleteEnabled":"Hapus Otomatis Diaktifkan",
"autodeleteDisabled":"Hapus Otomatis Dinonaktifkan"
"autodeleteDisabled":"Hapus Otomatis Dinonaktifkan",
"topicSet":"Topik Diganti",
"prioritySet":"Prioritas Berganti",
"priorityGet":"Prioritas Tiket",
"transfer":"Tiket Ditransfer"
},
"descriptions":{
"create":"Tiket kamu telah dibuat. Klik tombol di bawah ini untuk mengaksesnya!",
@@ -180,7 +197,12 @@
"ticketMessageLimit":"Anda hanya dapat membuat {0} tiket pada saat yang bersamaan!",
"ticketMessageAutoclose":"Tiket ini akan ditutup secara otomatis ketika tidak aktif selama {0} jam!",
"ticketMessageAutodelete":"Tiket ini akan dihapus secara otomatis ketika tidak aktif selama {0} hari!",
"panelReady":"Anda dapat menemukan panel di bawah ini!\nPesan ini sekarang dapat dihapus!"
"panelReady":"Panel ini tersedia dalam pesan tindak lanjut!\nPesan ini sekarang dapat dihapus!",
"topicSet":"Topik kanal ini telah diubah oleh {0}!",
"prioritySet":"Prioritas tiket ini telah diubah menjadi {0} oleh {1}!",
"priorityGet":"Prioritas tiket ini pada saat ini adalah {0}.",
"transfer":"Status kepemilikan tiket telah berhasil dipindahkan dari {0} ke {1} oleh {2}!"
},
"modal":{
"closePlaceholder":"Mengapa kamu menutup tiket ini?",
@@ -220,7 +242,14 @@
"blacklistRemoveLog":"{0} dihapus dari daftar hitam oleh {1}!",
"blacklistAddDm":"Anda telah masuk ke dalam daftar hitam di server kami!\nMulai sekarang, Anda tidak dapat membuat tiket!",
"blacklistRemoveDm":"Kamu telah dihapus dari daftar hitam di server kami!\nSekarang Anda dapat membuat tiket lagi!",
"clearLog":"Tiket {0} telah dihapus oleh {1}!"
"clearLog":"Tiket {0} telah dihapus oleh {1}!",
"transferLog":"Kepemilikan tiket ini telah berhasil dipindahkan dari {0} ke {1} oleh {2}",
"transferDm":"Kepemilikan tiket kamu sudah dialihkan dari {0} ke {1} di server kami!",
"prioritySetLog":"Prioritas tiket ini telah diubah menjadi {0} oleh {1}!",
"prioritySetDm":"Prioritas tiket kamu telah diubah menjadi {0} di server kami!",
"roleUpdateLog":"{0} telah memperbarui status mereka!",
"roleUpdateDm":"Status kamu di server kami telah diperbarui!"
}
},
"transcripts":{
@@ -241,7 +270,19 @@
"retry":"Coba Ulang",
"continue":"Hapus Tanpa Transkrip",
"backup":"Membuat Transkrip Cadangan",
"error":"Terjadi kesalahan saat mencoba membuat transkrip.\nApa yang ingin Anda lakukan?\n\nTiket ini tidak akan dihapus hingga Anda mengeklik salah satu tombol ini."
"error":"Terjadi kesalahan saat mencoba membuat transkrip.\nApa yang ingin Anda lakukan?\n\nTiket ini tidak akan dihapus hingga Anda mengeklik salah satu tombol ini.",
"title":"Kesalahan Dalam Pembuatan Transkrip"
},
"text":{
"messagesTitle":"PESAN",
"embedTitle":"SEMATAN",
"fileTitle":"BERKAS",
"fieldsTitle":"BIDANG",
"reactionsTitle":"REAKSI",
"statsTitle":"STATUS",
"emptyContent":"<isi dokumen kosong>",
"noTitle":"<tidak-ada-judul>",
"noDesc":"<tidak-ada-deskripsi>"
}
},
"errors":{
@@ -257,7 +298,8 @@
"unknownPanel":"Panel Tidak Diketahui",
"notInGuild":"Tidak Berada Di Dalam Server",
"channelRename":"Tidak Dapat Mengganti Nama Saluran",
"busy":"Tidak Sedang Sibuk"
"busy":"Tidak Sedang Sibuk",
"permissionError":"Terjadi Kesalahan Hak Akses"
},
"descriptions":{
"askForInfo":"Hubungi pemilik bot ini untuk info lebih lanjut!",
@@ -272,15 +314,18 @@
"noPermissionsCooldown":"Anda tidak diperbolehkan menggunakan {0} karena kamu sedang dalam cooldown!",
"noPermissionsBlacklist":"Anda tidak diperbolehkan menggunakan {0} karena Anda telah masuk dalam daftar hitam!",
"noPermissionsLimitGlobal":"Anda tidak diizinkan untuk membuat tiket karena server telah mencapai batas maksimal tiket!",
"noPermissionsLimitGlobalPengguna":"Anda tidak dapat membuat tiket karena Anda telah mencapai batas maksimal tiket!",
"noPermissionsLimitGlobalUser":"Anda tidak dapat membuat tiket karena Anda telah mencapai batas maksimal tiket!",
"noPermissionsLimitOption":"Anda tidak diizinkan untuk membuat tiket karena server telah mencapai batas maksimal tiket untuk opsi ini!",
"noPermissionsLimitOptionPengguna":"Anda tidak dapat membuat tiket karena Anda telah mencapai batas maksimal tiket untuk opsi ini!",
"noPermissionsLimitOptionUser":"Anda tidak dapat membuat tiket karena Anda telah mencapai batas maksimal tiket untuk opsi ini!",
"unknownTicket":"Gunakan perintah ini lagi dalam tiket yang valid!",
"deprecatedTicket":"Saluran saat ini bukanlah tiket yang valid! Ini mungkin merupakan tiket dari versi Open Tiket yang lama!",
"notInGuild":"Ini {0} tidak berfungsi di DM! Silakan coba lagi di server!",
"channelRename":"Karena batasan rasio Discord, saat ini bot tidak mungkin mengganti nama saluran. Saluran akan secara otomatis diganti namanya dalam waktu 10 menit jika bot tidak di-boot ulang.",
"channelRenameSource":"Sumber dari masalah: {0}",
"busy":"Dapat dapat menggunakan {0}!\nTiket sedang diproses oleh bot.\n\nMohon coba beberapa saat lagi!"
"busy":"Dapat dapat menggunakan {0}!\nTiket sedang diproses oleh bot.\n\nMohon coba beberapa saat lagi!",
"closeBeforeMessage":"Tiket ini tidak dapat ditutup ataupun dihapus sebelum pesan dikirim oleh seseorang.",
"closeBeforeAdminMessage":"Tiket ini tidak dapat ditutup ataupun dihapus sebelum admin tiket atau anggota tim dukungan mengirimkan pesan.",
"unableToCreateTicket":"Anda tidak dapat membuat tiket.."
},
"optionInvalidReasons":{
"stringRegex":"Nilai tidak sesuai pola!",
@@ -331,7 +376,6 @@
"added":"Ditambahkan",
"removed":"Dikeluarkan",
"filter":"Penyaring",
"claimedBy":"Diklaim oleh {0}",
"method":"Metode",
"type":"Tipe",
"blacklisted":"Daftar hitam",
@@ -355,7 +399,27 @@
"status":"Status",
"claimed":"Diklaim",
"pinned":"Disematkan",
"creationDate":"Tanggal Pembuatan"
"creationDate":"Tanggal Pembuatan",
"noone":"Tidak Seorangpun",
"open":"Buka",
"closed":"Tutup",
"priority":"Prioritas",
"participants":"Para Partisipan",
"yes":"Ya",
"no":"Tidak",
"option":"Opsi",
"topic":"Topik",
"uptime":"Waktu Aktif Sistem",
"messages":"Pesan",
"embeds":"Sematan",
"files":"Berkas",
"components":"Komponen",
"cooldown":"Cooldown",
"maxTickets":"Tiket Maksimum",
"admins":"Admin",
"roles":"Hak",
"size":"Ukuran"
},
"lowercase":{
"text":"teks",
@@ -384,6 +448,7 @@
"unclaim":"Batalkan klaim tiket.",
"pin":"Sematkan sebuah tiket.",
"unpin":"Batalkan penyematan tiket.",
"move":"Pindahkan tiket.",
"moveId":"Penanda opsi yang ingin anda pindahkan.",
"rename":"Ganti nama tiket.",
@@ -392,6 +457,7 @@
"addUser":"Pengguna yang akan ditambahkan.",
"remove":"Keluarkan seseorang dari tiket.",
"removeUser":"Pengguna yang akan dikeluarkan.",
"blacklist":"Kelola the Tiket blacklist.",
"blacklistView":"Lihat daftar yang masuk ke dalam daftar hitam.",
"blacklistAdd":"Tambahkan seseorang ke dalam daftar hitam.",
@@ -405,6 +471,7 @@
"statsUserUser":"Pengguna yang akan dilihat.",
"statsTicket":"Lihat status dari sebuah tiket di server.",
"statsTicketTicket":"Tiket yang akan dilihat.",
"clear":"Hapus banyak tiket sekaligus.",
"clearFilter":"Filter untuk menghapus tiket.",
"clearFilters":{
@@ -417,6 +484,7 @@
"unpin":"Pembatalan penyematan",
"autoclose":"Ditutup otomatis"
},
"autoclose":"Kelola penutupan otomatis tiket.",
"autocloseDisable":"Non-aktifkan penutupan otomatis.",
"autocloseEnable":"Aktifkan penutupan otomatis.",
@@ -424,7 +492,19 @@
"autodelete":"Kelola penghapusan otomatis tiket.",
"autodeleteDisable":"Non-aktifkan penghapusan otomatis.",
"autodeleteEnable":"Aktifkan penghapusan otomatis.",
"autodeleteEnableTime":"Lama waktu saluran tidak aktif sebelum dihapus."
"autodeleteEnableTime":"Lama waktu saluran tidak aktif sebelum dihapus.",
"topic":"Kelola topik kanal tiket.",
"topicSet":"Tentukan topik kanal tiket.",
"topicValue":"Topik baru kanal ini.",
"topicList":"Dapatkan informasi lengkap tentang semua tiket beserta topik dan statistiknya.",
"priority":"Kelola prioritas tiket.",
"prioritySet":"Tentukan prioritas tiket.",
"priorityValue":"Prioritas kanal.",
"priorityGet":"Dapatkan prioritas tiket.",
"priorityList":"Dapatkan daftar semua tiket beserta status prioritasnya.",
"transfer":"Alihkan kepemilikan tiket dari satu pengguna ke pengguna lain..",
"transferUser":"Pengguna yang akan ditransferkan."
},
"helpMenu":{
"help":"Dapatkan daftar perintah yang tersedia.",
@@ -452,7 +532,16 @@
"autocloseDisable":"Nonaktifkan penutupan otomatis.",
"autocloseEnable":"Aktifkan penutupan otomatis.",
"autodeleteDisable":"Non-aktifkan penghapusan otomatis.",
"autodeleteEnable":"Aktifkan penghapusan otomatis."
"autodeleteEnable":"Aktifkan penghapusan otomatis.",
"categories":{
"general":"Perintah Umum",
"basicTicket":"Perintah Dasar Tiket",
"advancedTicket":"Perintah Lanjutan Ticket",
"userTicket":"Perintah Tiket untuk Pengguna",
"admin":"Perintah Admin",
"advanced":"Perintah Lanjutan",
"extra":"Perintah Tambahan"
}
},
"stats":{
"scopes":{
@@ -460,7 +549,8 @@
"system":"Status Sistem",
"user":"Status Pengguna",
"ticket":"Status Tiket",
"participants":"Partisipan"
"participants":"Partisipan",
"messages":"Pesan"
},
"properties":{
"ticketsCreated":"Tiket Dibuat",
@@ -472,7 +562,47 @@
"ticketsPinned":"Tiket Disematkan",
"ticketsMoved":"Tickets Dipindahkan",
"usersBlacklisted":"Pengguna Masuk Ke Dalam Daftar Hitam",
"transcriptsCreated":"Transkrip Dibuat"
"transcriptsCreated":"Transkrip Dibuat",
"ticketsAutodeleted":"Tiket Dihapus Secara Otomatis",
"ticketsTransferred":"Tiket Dipindahkan",
"ticketVolume":"Kapasitas Tiket",
"averageTickets":"Rata-rata Tiket per Pengguna",
"currentTickets":"Tiket Saat Ini",
"age":"Umur Tiket",
"responseTime":"Waktu Respons",
"resolutionTime":"Resolution Time",
"createdOn":"Dibuat Pada",
"createdBy":"Dibuat Oleh",
"closedOn":"Ditutup Pada",
"closedBy":"Ditutup Oleh",
"claimedOn":"Diklaim Pada",
"claimedBy":"Diklaim Oleh",
"pinnedOn":"Disematkan Pada",
"pinnedBy":"Disematkan Oleh",
"deletedOn":"Dihapus Pada",
"deletedBy":"Dihapus Oleh"
},
"roles":{
"developer":"Pembuat",
"serverOwner":"Pemilik Server",
"serverAdmin":"Admin Server",
"moderator":"Tim Moderator",
"support":"Tim Pendukung",
"member":"Anggota"
}
},
"panel":{
"selectTicket":"Pilih tiket kamu",
"selectRole":"Pilih status kamu",
"selectOption":"Pilih opsi kamu"
},
"priorities":{
"urgent":"Sangat Penting",
"veryHigh":"Sangat Tinggi",
"high":"Tinggi",
"normal":"Normal",
"low":"Rendah",
"veryLow":"Sangat Rendah",
"none":"Tidak Ada"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["fraden1mvp."],
"lastedited":"08/10/2024",
"language":"Italian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Japanese",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Korean",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Kurdish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION": {
"otversion": "v4.0.7",
"otversion": "v4.1.0",
"translators": ["NoOneNook"],
"lastedited": "25/03/2025",
"language": "Latvian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["TsgIndrius"],
"lastedited":"26/01/2025",
"language":"Lithuanian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION": {
"otversion": "v4.0.7",
"otversion": "v4.1.0",
"translators": ["NoOneNook"],
"lastedited": "25/03/2025",
"language": "Norwegian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["dysashop","zhavis"],
"lastedited":"28/05/2025",
"language":"Persian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["DanoGlez"],
"lastedited":"28/01/2025",
"language":"Polish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["quiradon"],
"lastedited":"20/08/2024",
"language":"Portuguese",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["SankeDev"],
"lastedited":"27/08/2024",
"language":"Romanian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION": {
"otversion": "v4.0.7",
"otversion": "v4.1.0",
"translators": ["NoOneNook"],
"lastedited": "25/03/2025",
"language": "Russian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Simplified Chainese",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Solvenian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["Redactado","Josuens"],
"lastedited":"22/08/2024",
"language":"Spanish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION": {
"otversion": "v4.0.7",
"otversion": "v4.1.0",
"translators": ["NoOneNook"],
"lastedited": "25/03/2025",
"language": "Svenska",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["HanumeshGupta"],
"lastedited":"19/06/2025",
"language":"Tamil",
+534 -404
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION": {
"otversion": "v4.0.7",
"otversion": "v4.1.0",
"translators": ["palestinian"],
"lastedited": "26/11/2024",
"language": "Turkish",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["Anderskiy"],
"lastedited":"31/08/2024",
"language":"Ukrainian",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"_TRANSLATION":{
"otversion":"v4.0.7",
"otversion":"v4.1.0",
"translators":["ngocdiep2006"],
"lastedited":"07/04/2025",
"language":"Vietnamese",
+11 -6
View File
@@ -1,7 +1,7 @@
{
"name": "open-ticket",
"author": "DJdj Development",
"version": "4.0.7",
"version": "4.1.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",
@@ -14,19 +14,24 @@
"scripts": {
"build": "node index.js --compile-only",
"start": "node index.js",
"setup": "node index.js --cli",
"startnc": "node index.js --no-compile",
"test": "node index.js --dev-config --dev-database",
"testnc": "node index.js --no-compile --dev-config --dev-database",
"docs": "npx typedoc --options .docs/typedoc-config.json && node .docs/createDocs.js"
"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"
},
"type": "commonjs",
"license": "GPL-3.0-only",
"dependencies": {
"@discordjs/rest": "^2.4.2",
"@types/node": "^22.5.0",
"@types/terminal-kit": "^2.5.7",
"ansis": "^2.3.0",
"discord.js": "^14.19.3",
"formatted-json-stringify": "^1.1.0",
"discord.js": "^14.22.1",
"formatted-json-stringify": "^1.2.1",
"terminal-kit": "^3.1.2",
"typescript": "^5.5.4"
},
"repository": {
+3
View File
@@ -54,6 +54,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:add-message").build(source,{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) => {
const {guild,channel,user,ticket,reason,data} = params
+3
View File
@@ -65,6 +65,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:claim-message").build(source,{guild,channel,user,ticket,reason})).message)
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) => {
const {guild,channel,user,ticket,reason} = params
+37 -4
View File
@@ -5,6 +5,7 @@ import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerActions = async () => {
opendiscord.actions.add(new api.ODAction("opendiscord:close-ticket"))
@@ -17,10 +18,15 @@ export const registerActions = async () => {
//update ticket
ticket.get("opendiscord:closed").value = true
if (source == "autoclose") ticket.get("opendiscord:autoclosed").value = true
ticket.get("opendiscord:open").value = false
ticket.get("opendiscord:closed-by").value = user.id
ticket.get("opendiscord:closed-on").value = new Date().getTime()
ticket.get("opendiscord:reopened").value = false
ticket.get("opendiscord:reopened-by").value = null
ticket.get("opendiscord:reopened-on").value = null
if (source == "autoclose") ticket.get("opendiscord:autoclosed").value = true
ticket.get("opendiscord:open").value = false
ticket.get("opendiscord:busy").value = true
//update stats
@@ -123,6 +129,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:close-message").build(source,{guild,channel,user,ticket,reason})).message)
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) => {
const {guild,channel,user,ticket,reason} = params
@@ -206,7 +215,7 @@ export const registerVerifyBars = async () => {
}
//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:opendiscord.languages.getTranslation("errors.actionInvalid.close"),layout:"simple"}))
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
@@ -214,6 +223,18 @@ export const registerVerifyBars = async () => {
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 (!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"){
@@ -293,7 +314,7 @@ export const registerVerifyBars = async () => {
}
//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:opendiscord.languages.getTranslation("errors.actionInvalid.close"),layout:"simple"}))
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
@@ -301,6 +322,18 @@ export const registerVerifyBars = async () => {
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 (!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"){
+48 -20
View File
@@ -5,6 +5,7 @@ import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerActions = async () => {
opendiscord.actions.add(new api.ODAction("opendiscord:create-ticket"))
@@ -19,8 +20,8 @@ export const registerActions = async () => {
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 channelDescription = option.get("opendiscord:channel-description").value
const channelSuffix = await opendiscord.options.suffix.getSuffixFromOption(option,user)
const channelTopicText = option.get("opendiscord:channel-topic").value
const channelSuffix = await opendiscord.options.suffix.getSuffixFromOption(option,user,guild)
const channelName = channelPrefix+channelSuffix
//handle category
@@ -73,7 +74,7 @@ export const registerActions = async () => {
permissions.push({
type:discord.OverwriteType.Role,
id:admin,
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","ManageMessages"],
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","ManageMessages","PinMessages","EmbedLinks"],
deny:[]
})
})
@@ -82,7 +83,7 @@ export const registerActions = async () => {
permissions.push({
type:discord.OverwriteType.Role,
id:admin,
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","ManageMessages"],
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","ManageMessages","PinMessages","EmbedLinks"],
deny:[]
})
})
@@ -93,29 +94,16 @@ export const registerActions = async () => {
type:discord.OverwriteType.Role,
id:admin,
allow:["ViewChannel","ReadMessageHistory"],
deny:["SendMessages","AddReactions","AttachFiles","SendPolls"]
deny:["SendMessages","AddReactions","AttachFiles","SendPolls","PinMessages"]
})
})
permissions.push({
type:discord.OverwriteType.Member,
id:user.id,
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory"],
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","EmbedLinks","PinMessages"],
deny:[]
})
//create channel
const channel = await guild.channels.create({
type:discord.ChannelType.GuildText,
name:channelName,
nsfw:false,
topic:channelDescription,
parent:category,
reason:"Ticket Created By "+user.displayName,
permissionOverwrites:permissions
})
await opendiscord.events.get("afterTicketChannelCreated").emit([option,channel,user])
//create participants
const participants: {type:"role"|"user",id:string}[] = []
permissions.forEach((permission,index) => {
@@ -125,12 +113,42 @@ export const registerActions = async () => {
participants.push({type,id})
})
//manage slowmode
const slowMode = option.get("opendiscord:slowmode-enabled").value ? option.get("opendiscord:slowmode-seconds").value : undefined
//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(", "))
//create channel
const channel = await guild.channels.create({
type:discord.ChannelType.GuildText,
name:channelName,
nsfw:false,
topic:(channelTopics.length > 0) ? channelTopics.join(" • ") : undefined,
parent:category,
reason:"Ticket Created By "+user.displayName,
permissionOverwrites:permissions,
rateLimitPerUser:slowMode
})
await opendiscord.events.get("afterTicketChannelCreated").emit([option,channel,user])
//create ticket
const ticket = new api.ODTicket(channel.id,option,[
new api.ODTicketData("opendiscord:busy",false),
new api.ODTicketData("opendiscord:ticket-message",null),
new api.ODTicketData("opendiscord:participants",participants),
new api.ODTicketData("opendiscord:channel-suffix",channelSuffix),
new api.ODTicketData("opendiscord:previous-creators",[]),
new api.ODTicketData("opendiscord:open",true),
new api.ODTicketData("opendiscord:opened-by",user.id),
@@ -138,6 +156,9 @@ export const registerActions = async () => {
new api.ODTicketData("opendiscord:closed",false),
new api.ODTicketData("opendiscord:closed-by",null),
new api.ODTicketData("opendiscord:closed-on",null),
new api.ODTicketData("opendiscord:reopened",false),
new api.ODTicketData("opendiscord:reopened-by",null),
new api.ODTicketData("opendiscord:reopened-on",null),
new api.ODTicketData("opendiscord:claimed",false),
new api.ODTicketData("opendiscord:claimed-by",null),
new api.ODTicketData("opendiscord:claimed-on",null),
@@ -155,7 +176,11 @@ export const registerActions = async () => {
new api.ODTicketData("opendiscord:autodelete-enabled",option.get("opendiscord:autodelete-enable-days").value),
new api.ODTicketData("opendiscord:autodelete-days",(option.get("opendiscord:autodelete-enable-days").value ? option.get("opendiscord:autodelete-days").value : 0)),
new api.ODTicketData("opendiscord:answers",answers)
new api.ODTicketData("opendiscord:answers",answers),
new api.ODTicketData("opendiscord:priority",-1),
new api.ODTicketData("opendiscord:topic",option.get("opendiscord:channel-topic").value),
new api.ODTicketData("opendiscord:message-sent",false),
new api.ODTicketData("opendiscord:admin-message-sent",false),
])
//manage stats
@@ -186,6 +211,9 @@ export const registerActions = async () => {
ticket.get("opendiscord:ticket-message").value = msg.id
//pin ticket message (if required)
if (generalConfig.data.system.pinFirstTicketMessage && msg.pinnable) await msg.pin("Ticket Message")
//manage stats
await opendiscord.stats.get("opendiscord:ticket").setStat("opendiscord:messages-sent",ticket.id.value,1,"increase")
+81
View File
@@ -5,6 +5,7 @@ import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerActions = async () => {
opendiscord.actions.add(new api.ODAction("opendiscord:delete-ticket"))
@@ -121,6 +122,14 @@ export const registerVerifyBars = async () => {
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.delete
//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()
}
}
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:[]}))
@@ -167,6 +176,18 @@ export const registerVerifyBars = async () => {
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 (!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"){
@@ -206,6 +227,14 @@ export const registerVerifyBars = async () => {
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.delete
//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()
}
}
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:[]}))
@@ -252,6 +281,18 @@ export const registerVerifyBars = async () => {
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 (!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"){
@@ -295,6 +336,14 @@ export const registerVerifyBars = async () => {
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.delete
//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()
}
}
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:[]}))
@@ -341,6 +390,18 @@ export const registerVerifyBars = async () => {
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 (!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"){
@@ -384,6 +445,14 @@ export const registerVerifyBars = async () => {
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.delete
//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()
}
}
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:[]}))
@@ -430,6 +499,18 @@ export const registerVerifyBars = async () => {
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 (!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"){
+11 -8
View File
@@ -28,8 +28,7 @@ export const registerActions = async () => {
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 channelDescription = ticket.option.get("opendiscord:channel-description").value
const channelName = channelPrefix+channelSuffix
const channelTopic = ticket.option.get("opendiscord:channel-topic").value
//handle category
let category: string|null = null
@@ -155,17 +154,18 @@ export const registerActions = async () => {
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(channelName),2500,(err) => {
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:channelName})).message)
await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:error-channel-rename").build("ticket-move",{guild,channel,user,originalName,newName:newName})).message)
}
try{
if (channel.type == discord.ChannelType.GuildText) channel.setTopic(channelDescription)
}catch{}
//update ticket message
const ticketMessage = await opendiscord.tickets.getTicketMessage(ticket)
@@ -173,7 +173,7 @@ export const registerActions = async () => {
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",[
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},
@@ -187,6 +187,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:move-message").build(source,{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) => {
const {guild,channel,user,ticket,reason,data} = params
+7 -1
View File
@@ -31,8 +31,11 @@ export const registerActions = async () => {
}
//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 = "📌"+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")
@@ -61,6 +64,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:pin-message").build(source,{guild,channel,user,ticket,reason})).message)
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) => {
const {guild,channel,user,ticket,reason} = params
+15
View File
@@ -4,6 +4,8 @@
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
opendiscord.actions.add(new api.ODAction("opendiscord:reaction-role"))
opendiscord.actions.get("opendiscord:reaction-role").workers.add([
@@ -81,6 +83,19 @@ 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) => {
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)){
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}))
}
//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}))
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
const {guild,user,option} = params
opendiscord.log(user.displayName+" updated his roles!","info",[
+3
View File
@@ -48,6 +48,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:remove-message").build(source,{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) => {
const {guild,channel,user,ticket,reason,data} = params
+8 -1
View File
@@ -16,9 +16,13 @@ export const registerActions = async () => {
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 ?? ""
const originalName = channel.name
const newName = pinEmoji+priorityEmoji+utilities.trimEmojis(data)
try{
await utilities.timedAwait(channel.setName(data),2500,(err) => {
await utilities.timedAwait(channel.setName(newName),2500,(err) => {
opendiscord.log("Failed to rename channel on ticket rename","error")
})
}catch(err){
@@ -45,6 +49,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:rename-message").build(source,{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) => {
const {guild,channel,user,ticket,reason,data} = params
+16 -2
View File
@@ -16,13 +16,24 @@ export const registerActions = async () => {
await opendiscord.events.get("onTicketReopen").emit([ticket,user,channel,reason])
//update ticket
ticket.get("opendiscord:reopened").value = true
ticket.get("opendiscord:reopened-by").value = user.id
ticket.get("opendiscord:reopened-on").value = new Date().getTime()
ticket.get("opendiscord:closed").value = false
ticket.get("opendiscord:open").value = true
ticket.get("opendiscord:autoclosed").value = false
ticket.get("opendiscord:closed-by").value = null
ticket.get("opendiscord:closed-on").value = null
ticket.get("opendiscord:autoclosed").value = false
ticket.get("opendiscord:open").value = true
ticket.get("opendiscord:busy").value = true
if (generalConfig.data.system.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")
@@ -149,6 +160,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:reopen-message").build(source,{guild,channel,user,ticket,reason})).message)
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) => {
const {guild,channel,user,ticket,reason} = params
+154
View File
@@ -0,0 +1,154 @@
///////////////////////////////////////
//TICKET TRANSFER SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
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) => {
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!")
const oldCreator = await opendiscord.tickets.getTicketUser(ticket,"creator") ?? opendiscord.client.client.user
await opendiscord.events.get("onTicketTransfer").emit([ticket,user,channel,oldCreator,newCreator,reason])
//update ticket
const oldCreatorId = ticket.get("opendiscord:opened-by").value
if (oldCreatorId){
ticket.get("opendiscord:previous-creators").value.push(oldCreatorId)
ticket.get("opendiscord:previous-creators").refreshDatabase()
}
if (!ticket.get("opendiscord:participants").value.find((p) => p.type == "user" && p.id == newCreator.id)){
ticket.get("opendiscord:participants").value.push({type:"user",id:newCreator.id})
ticket.get("opendiscord:participants").refreshDatabase()
}
ticket.get("opendiscord:opened-by").value = newCreator.id
if (["user-name","user-nickname","user-id"].includes(ticket.option.get("opendiscord:channel-suffix").value)){
const newSuffix = await opendiscord.options.suffix.getSuffixFromOption(ticket.option,newCreator,guild)
if (newSuffix) ticket.get("opendiscord:channel-suffix").value = newSuffix
}
//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
//handle permissions
const permissions: discord.OverwriteResolvable[] = [{
type:discord.OverwriteType.Role,
id:guild.roles.everyone.id,
allow:[],
deny:["ViewChannel","SendMessages","ReadMessageHistory"]
}]
const globalAdmins = opendiscord.configs.get("opendiscord:general").data.globalAdmins
const optionAdmins = ticket.option.get("opendiscord:admins").value
const readonlyAdmins = ticket.option.get("opendiscord:admins-readonly").value
globalAdmins.forEach((admin) => {
permissions.push({
type:discord.OverwriteType.Role,
id:admin,
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","ManageMessages"],
deny:[]
})
})
optionAdmins.forEach((admin) => {
if (globalAdmins.includes(admin)) return
permissions.push({
type:discord.OverwriteType.Role,
id:admin,
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory","ManageMessages"],
deny:[]
})
})
readonlyAdmins.forEach((admin) => {
if (globalAdmins.includes(admin)) return
if (optionAdmins.includes(admin)) return
permissions.push({
type:discord.OverwriteType.Role,
id:admin,
allow:["ViewChannel","ReadMessageHistory"],
deny:["SendMessages","AddReactions","AttachFiles","SendPolls"]
})
})
//transfer all old user-participants over to the new ticket (creator & participants)
ticket.get("opendiscord:participants").value.forEach((p) => {
if (p.type == "user") permissions.push({
type:discord.OverwriteType.Member,
id:p.id,
allow:["ViewChannel","SendMessages","AddReactions","AttachFiles","SendPolls","ReadMessageHistory"],
deny:[]
})
})
try{
await channel.permissionOverwrites.set(permissions)
}catch{
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){
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}
])
opendiscord.debugfile.writeErrorMessage(new api.ODError(e,"uncaughtException"))
}
}
//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)
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) => {
const {guild,channel,user,ticket,newCreator,reason} = params
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
const {guild,channel,user,ticket,newCreator} = params
opendiscord.log(user.displayName+" transferred a ticket to '"+newCreator.displayName+"'!","info",[
{key:"user",value:user.username},
{key:"userid",value:user.id,hidden:true},
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"reason",value:params.reason ?? "/"},
{key:"method",value:source}
])
})
])
opendiscord.actions.get("opendiscord:transfer-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
})
}
+3
View File
@@ -94,6 +94,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:unclaim-message").build(source,{guild,channel,user,ticket,reason})).message)
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) => {
const {guild,channel,user,ticket,reason} = params
+7 -3
View File
@@ -22,9 +22,11 @@ export const registerActions = async () => {
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
if (originalName.startsWith("📌")){
const newName = originalName.replace("📌","");
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")
@@ -32,7 +34,6 @@ export const registerActions = async () => {
}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)
@@ -54,6 +55,9 @@ export const registerActions = async () => {
if (params.sendMessage) await channel.send((await opendiscord.builders.messages.getSafe("opendiscord:unpin-message").build(source,{guild,channel,user,ticket,reason})).message)
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) => {
const {guild,channel,user,ticket,reason} = params
+65
View File
@@ -0,0 +1,65 @@
///////////////////////////////////////
//TICKET TOPIC SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerActions = async () => {
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) => {
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!")
const oldPriority = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value)
await opendiscord.events.get("onTicketPriorityChange").emit([ticket,user,channel,oldPriority,newPriority,reason])
//update ticket
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)
}
//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)
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) => {
const {guild,channel,user,ticket} = params
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
const {guild,channel,user,ticket,newPriority} = params
opendiscord.log(user.displayName+" changed the priority of a ticket!","info",[
{key:"user",value:user.username},
{key:"userid",value:user.id,hidden:true},
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"priority",value:newPriority.id.value},
{key:"method",value:source}
])
})
])
opendiscord.actions.get("opendiscord:update-ticket-priority").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
})
}
+70
View File
@@ -0,0 +1,70 @@
///////////////////////////////////////
//TICKET TOPIC SYSTEM
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerActions = async () => {
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) => {
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!")
const oldTopic = ticket.get("opendiscord:topic").value
if (newTopic) await opendiscord.events.get("onTicketTopicChange").emit([ticket,user,channel,oldTopic,newTopic])
//update ticket
ticket.get("opendiscord:busy").value = true
if (newTopic) ticket.get("opendiscord:topic").value = newTopic
//get ticket data
const closed = ticket.get("opendiscord:closed").value
const claimedBy = ticket.get("opendiscord:claimed-by").value
const pinned = ticket.get("opendiscord:pinned").value
const creator = ticket.get("opendiscord:opened-by").value ?? opendiscord.client.client.user.id
//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(", "))
//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)
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) => {
const {guild,channel,user,ticket} = params
}),
new api.ODWorker("opendiscord:logs",0,(instance,params,source,cancel) => {
const {guild,channel,user,ticket,newTopic} = params
if (newTopic) opendiscord.log(user.displayName+" changed the topic of a ticket!","info",[
{key:"user",value:user.username},
{key:"userid",value:user.id,hidden:true},
{key:"channel",value:"#"+channel.name},
{key:"channelid",value:channel.id,hidden:true},
{key:"topic",value:newTopic},
{key:"method",value:source}
])
})
])
opendiscord.actions.get("opendiscord:update-ticket-topic").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
})
}
+2 -2
View File
@@ -277,7 +277,7 @@ const ticketButtons = () => {
instance.setMode("button")
instance.setCustomId("od:pin-ticket_"+source)
instance.setColor("gray")
instance.setEmoji("📌")
instance.setEmoji(generalConfig.data.system.pinEmoji)
instance.setLabel(lang.getTranslation("actions.buttons.pin"))
})
)
@@ -291,7 +291,7 @@ const ticketButtons = () => {
instance.setMode("button")
instance.setCustomId("od:unpin-ticket_"+source)
instance.setColor("gray")
instance.setEmoji("📌")
instance.setEmoji(generalConfig.data.system.pinEmoji)
instance.setLabel(lang.getTranslation("actions.buttons.unpin"))
})
)
+153 -38
View File
@@ -20,6 +20,7 @@ export const registerAllEmbeds = async () => {
roleEmbeds()
clearEmbeds()
autoEmbeds()
extraEmbeds()
}
/**Utility function to get the translated "method" from the source. Mostly used in error embeds. */
@@ -37,12 +38,12 @@ const errorEmbeds = () => {
embeds.add(new api.ODEmbed("opendiscord:error"))
embeds.get("opendiscord:error").workers.add(
new api.ODWorker("opendiscord:error",0,async (instance,params,source) => {
const {user,error,layout} = params
const {user,error,layout,customTitle} = params
const method = getMethodFromSource(source)
instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("❌",lang.getTranslation("errors.titles.internalError")))
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 : "")
if (layout == "advanced" && error) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+error+"```"})
@@ -329,7 +330,7 @@ const errorEmbeds = () => {
new api.ODWorker("opendiscord:error-channel-rename",0,async (instance,params,source) => {
const {channel,user,originalName,newName} = params
const method = (source == "ticket-move" || source == "ticket-pin" || source == "ticket-rename" || source == "ticket-unpin") ? source : getMethodFromSource(source)
const method = (source == "ticket-move" || source == "ticket-pin" || source == "ticket-rename" || source == "ticket-unpin" || source == "ticket-priority" || source == "ticket-transfer") ? source : getMethodFromSource(source)
instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("❌",lang.getTranslation("errors.titles.channelRename")))
@@ -412,14 +413,17 @@ const statsEmbeds = () => {
const scope = opendiscord.stats.get("opendiscord:ticket")
const participantsScope = opendiscord.stats.get("opendiscord:participants")
if (!scope || !participantsScope) return
const messagesScope = opendiscord.stats.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)
const messagesData = await messagesScope.render(scopeData.id.value,guild,channel,user)
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(scope.name)
instance.setDescription(data)
instance.addFields({name:participantsScope.name,value:participantsData,inline:false})
instance.addFields({name:participantsScope.name,value:participantsData,inline:true})
instance.addFields({name:messagesScope.name,value:messagesData,inline:true})
})
)
@@ -594,6 +598,9 @@ const ticketEmbeds = () => {
if (embedOptions.description) instance.setDescription(embedOptions.description)
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)
const answers = ticket.get("opendiscord:answers").value
answers.forEach((answer) => {
if (!answer.value || answer.value.length == 0) return
@@ -610,7 +617,7 @@ const ticketEmbeds = () => {
if (ticket.get("opendiscord:claimed").value){
const claimUser = await opendiscord.tickets.getTicketUser(ticket,"claimer")
if (!claimUser) return
instance.setAuthor(lang.getTranslationWithParams("params.uppercase.claimedBy",[claimUser.displayName]),claimUser.displayAvatarURL())
instance.setAuthor(lang.getTranslation("stats.properties.claimedBy")+" "+claimUser.displayName,claimUser.displayAvatarURL())
}
})
)
@@ -625,7 +632,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🔒",lang.getTranslation("actions.titles.close")))
instance.setDescription(lang.getTranslation("actions.descriptions.close"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -639,7 +646,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🔓",lang.getTranslation("actions.titles.reopen")))
instance.setDescription(lang.getTranslation("actions.descriptions.reopen"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -653,7 +660,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🗑️",lang.getTranslation("actions.titles.delete")))
instance.setDescription(lang.getTranslation("actions.descriptions.delete"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -667,7 +674,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("👋",lang.getTranslation("actions.titles.claim")))
instance.setDescription(lang.getTranslation("actions.descriptions.claim"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -681,7 +688,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("↩️",lang.getTranslation("actions.titles.unclaim")))
instance.setDescription(lang.getTranslation("actions.descriptions.unclaim"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -693,9 +700,9 @@ const ticketEmbeds = () => {
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("📌",lang.getTranslation("actions.titles.pin")))
instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.pin")))
instance.setDescription(lang.getTranslation("actions.descriptions.pin"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -707,9 +714,9 @@ const ticketEmbeds = () => {
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("📌",lang.getTranslation("actions.titles.unpin")))
instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.unpin")))
instance.setDescription(lang.getTranslation("actions.descriptions.unpin"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -723,7 +730,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🔄",lang.getTranslation("actions.titles.rename")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.rename",["`#"+data+"`"]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -737,7 +744,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🔀",lang.getTranslation("actions.titles.move")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.move",["`"+data.get("opendiscord:name").value+"`"]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -751,7 +758,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("👤",lang.getTranslation("actions.titles.add")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.add",[discord.userMention(data.id)]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -765,7 +772,7 @@ const ticketEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("👤",lang.getTranslation("actions.titles.remove")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.remove",[discord.userMention(data.id)]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -797,10 +804,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("📌",lang.getTranslation("actions.titles.pin")))
instance.setTitle(utilities.emojiTitle(generalConfig.data.system.pinEmoji,lang.getTranslation("actions.titles.pin")))
instance.setDescription(lang.getTranslation("actions.logs.pinDm"))
}else if (mode == "unpin"){
instance.setTitle(utilities.emojiTitle("📌",lang.getTranslation("actions.titles.unpin")))
instance.setTitle(utilities.emojiTitle(generalConfig.data.system.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")))
@@ -831,10 +838,9 @@ const ticketEmbeds = () => {
instance.setTimestamp(new Date())
instance.addFields(
{name:lang.getTranslation("params.uppercase.ticket")+":",value:"```#"+(channel ? channel.name : "<unknown>")+"```",inline:false},
//TODO TRANSLATION!!!
{name:"Option"+":",value:"```"+(ticket.option.get("opendiscord:name").value)+"```",inline:false},
{name:lang.getTranslation("params.uppercase.option")+":",value:"```"+(ticket.option.get("opendiscord:name").value)+"```",inline:false},
)
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```",inline:false})
if (reason || generalConfig.data.system.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")))
@@ -852,10 +858,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("📌",lang.getTranslation("actions.titles.pin")))
instance.setTitle(utilities.emojiTitle(generalConfig.data.system.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("📌",lang.getTranslation("actions.titles.unpin")))
instance.setTitle(utilities.emojiTitle(generalConfig.data.system.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")))
@@ -908,7 +914,7 @@ const blacklistEmbeds = () => {
if (blacklist){
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.blacklistGetSuccess",[discord.userMention(data.id)]))
if (blacklist.reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+blacklist.reason+"```"})
if (blacklist.reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(blacklist.reason ?? "/")+"```"})
}else instance.setDescription("*"+lang.getTranslationWithParams("actions.descriptions.blacklistGetEmpty",[discord.userMention(data.id)])+"*")
})
@@ -924,7 +930,7 @@ const blacklistEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🚫",lang.getTranslation("actions.titles.blacklistAdd")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.blacklistAdd",[discord.userMention(data.id)]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -938,7 +944,7 @@ const blacklistEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🆓",lang.getTranslation("actions.titles.blacklistRemove")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.blacklistRemove",[discord.userMention(data.id)]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -955,7 +961,7 @@ const blacklistEmbeds = () => {
instance.setTitle(utilities.emojiTitle((mode == "add") ? "🚫" : "🆓",title))
instance.setTimestamp(new Date())
instance.setDescription(text)
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```",inline:false})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -974,7 +980,7 @@ const blacklistEmbeds = () => {
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setTimestamp(new Date())
instance.setDescription(text)
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```",inline:false})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
@@ -1074,11 +1080,11 @@ const transcriptEmbeds = () => {
const {guild,channel,user,ticket,compiler,reason} = params
instance.setColor(generalConfig.data.system.useRedErrorEmbeds ? "Red" : generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("❌","Transcript Error")) //TODO TRANSLATION!!!
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) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```",inline:false})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
@@ -1107,6 +1113,58 @@ const roleEmbeds = () => {
else instance.setDescription(lang.getTranslation("actions.descriptions.rolesEmpty"))
})
)
//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) => {
const {guild,user,role,result} = params
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🎨",lang.getTranslation("actions.titles.roles")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setTimestamp(new Date())
const newResult = result.filter((r) => r.action != null).sort((a,b) => {
if (a.action == "added" && b.action == "removed") return -1
else if (a.action == "removed" && b.action == "added") return 1
else return 0
}).map((r) => {
return (r.action == "added") ? "🟢 "+lang.getTranslation("params.uppercase.added")+" @"+r.role.name : "🔴 "+lang.getTranslation("params.uppercase.removed")+" @"+r.role.name
})
const baseDescription = lang.getTranslation("actions.logs.roleUpdateDm")
if (newResult.length > 0) instance.setDescription(baseDescription+"\n\n"+newResult.join("\n"))
else instance.setDescription(baseDescription+"\n"+lang.getTranslation("actions.descriptions.rolesEmpty"))
})
)
//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) => {
const {guild,user,role,result} = params
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🎨",lang.getTranslation("actions.titles.roles")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setTimestamp(new Date())
const newResult = result.filter((r) => r.action != null).sort((a,b) => {
if (a.action == "added" && b.action == "removed") return -1
else if (a.action == "removed" && b.action == "added") return 1
else return 0
}).map((r) => {
return (r.action == "added") ? "🟢 "+lang.getTranslation("params.uppercase.added")+" "+discord.roleMention(r.role.id) : "🔴 "+lang.getTranslation("params.uppercase.removed")+" "+discord.roleMention(r.role.id)
})
const baseDescription = lang.getTranslationWithParams("actions.logs.roleUpdateLog",[discord.userMention(user.id)])
if (newResult.length > 0) instance.setDescription(baseDescription+"\n\n"+newResult.join("\n"))
else instance.setDescription(baseDescription+"\n"+lang.getTranslation("actions.descriptions.rolesEmpty"))
})
)
}
const clearEmbeds = () => {
@@ -1117,7 +1175,7 @@ const clearEmbeds = () => {
const {guild,channel,user,filter,list} = params
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("⚠️","Clear Tickets")) //TODO TRANSLATION!!!
instance.setTitle(utilities.emojiTitle("⚠️",lang.getTranslation("actions.titles.clearTickets")))
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setTimestamp(new Date())
instance.setDescription(lang.getTranslation("actions.descriptions.clearVerify"))
@@ -1207,7 +1265,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("⏱️",lang.getTranslation("actions.titles.autocloseEnabled")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.autocloseEnabled",[time.toString()]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1221,7 +1279,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("⏱️",lang.getTranslation("actions.titles.autodeleteEnabled")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.autodeleteEnabled",[time.toString()]))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1235,7 +1293,7 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("⏱️",lang.getTranslation("actions.titles.autocloseDisabled")))
instance.setDescription(lang.getTranslation("actions.descriptions.autocloseDisabled"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
@@ -1249,7 +1307,64 @@ const autoEmbeds = () => {
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("⏱️",lang.getTranslation("actions.titles.autodeleteDisabled")))
instance.setDescription(lang.getTranslation("actions.descriptions.autodeleteDisabled"))
if (reason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+reason+"```"})
if (reason || generalConfig.data.system.alwaysShowReason) instance.addFields({name:lang.getTranslation("params.uppercase.reason")+":",value:"```"+(reason ?? "/")+"```"})
})
)
}
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) => {
const {user,topic} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("ℹ️",lang.getTranslation("actions.titles.topicSet")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.topicSet",[discord.userMention(user.id)]))
if (topic) instance.addFields({name:lang.getTranslation("params.uppercase.topic")+":",value:"```"+topic+"```"})
})
)
//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) => {
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 ?? "/")+"```"})
})
)
//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) => {
const {user,priority} = params
instance.setAuthor(user.displayName,user.displayAvatarURL())
instance.setColor(generalConfig.data.mainColor)
instance.setTitle(utilities.emojiTitle("🚨",lang.getTranslation("actions.titles.priorityGet")))
instance.setDescription(lang.getTranslationWithParams("actions.descriptions.priorityGet",["**"+priority.renderDisplayName()+"**"]))
})
)
//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) => {
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 ?? "/")+"```"})
})
)
}
+59 -2
View File
@@ -24,6 +24,7 @@ export const registerAllMessages = async () => {
roleMessages()
clearMessages()
autoMessages()
extraMessages()
}
const verifyBarMessages = () => {
@@ -332,8 +333,8 @@ const errorMessages = () => {
messages.add(new api.ODMessage("opendiscord:error"))
messages.get("opendiscord:error").workers.add(
new api.ODWorker("opendiscord:error",0,async (instance,params,source) => {
const {guild,channel,user,error,layout} = params
instance.addEmbed(await embeds.getSafe("opendiscord:error").build(source,{guild,channel,user,error,layout}))
const {guild,channel,user,error,layout,customTitle} = params
instance.addEmbed(await embeds.getSafe("opendiscord:error").build(source,{guild,channel,user,error,layout,customTitle}))
instance.setEphemeral(true)
})
)
@@ -964,6 +965,24 @@ const roleMessages = () => {
instance.setEphemeral(true)
})
)
//REACTION ROLE DM
messages.add(new api.ODMessage("opendiscord:reaction-role-dm"))
messages.get("opendiscord:reaction-role-dm").workers.add(
new api.ODWorker("opendiscord:reaction-role-dm",0,async (instance,params,source) => {
const {guild,user,role,result} = params
instance.addEmbed(await embeds.getSafe("opendiscord:reaction-role-dm").build(source,{guild,user,role,result}))
})
)
//REACTION ROLE LOGS
messages.add(new api.ODMessage("opendiscord:reaction-role-logs"))
messages.get("opendiscord:reaction-role-logs").workers.add(
new api.ODWorker("opendiscord:reaction-role-logs",0,async (instance,params,source) => {
const {guild,user,role,result} = params
instance.addEmbed(await embeds.getSafe("opendiscord:reaction-role-logs").build(source,{guild,user,role,result}))
})
)
}
const clearMessages = () => {
@@ -1055,3 +1074,41 @@ const autoMessages = () => {
})
)
}
const extraMessages = () => {
//TOPIC SET
messages.add(new api.ODMessage("opendiscord:topic-set"))
messages.get("opendiscord:topic-set").workers.add(
new api.ODWorker("opendiscord:topic-set",0,async (instance,params,source) => {
const {guild,channel,user,ticket,topic} = params
instance.addEmbed(await embeds.getSafe("opendiscord:topic-set").build(source,{guild,channel,user,ticket,topic}))
})
)
//PRIORITY SET
messages.add(new api.ODMessage("opendiscord:priority-set"))
messages.get("opendiscord:priority-set").workers.add(
new api.ODWorker("opendiscord:priority-set",0,async (instance,params,source) => {
const {guild,channel,user,ticket,priority,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:priority-set").build(source,{guild,channel,user,ticket,priority,reason}))
})
)
//PRIORITY GET
messages.add(new api.ODMessage("opendiscord:priority-get"))
messages.get("opendiscord:priority-get").workers.add(
new api.ODWorker("opendiscord:priority-get",0,async (instance,params,source) => {
const {guild,channel,user,ticket,priority} = params
instance.addEmbed(await embeds.getSafe("opendiscord:priority-get").build(source,{guild,channel,user,ticket,priority}))
})
)
//TRANSFER MESSAGE
messages.add(new api.ODMessage("opendiscord:transfer-message"))
messages.get("opendiscord:transfer-message").workers.add(
new api.ODWorker("opendiscord:transfer-message",0,async (instance,params,source) => {
const {guild,channel,user,ticket,oldCreator,newCreator,reason} = params
instance.addEmbed(await embeds.getSafe("opendiscord:transfer-message").build(source,{guild,channel,user,ticket,oldCreator,newCreator,reason}))
})
)
}
+32
View File
@@ -0,0 +1,32 @@
///////////////////////////////////////
//AUTOCOMPLETE COMMAND UTILS
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
export const registerAutocompleteResponders = async () => {
//PANEL ID AUTOCOMPLETE
opendiscord.responders.autocomplete.add(new api.ODAutocompleteResponder("opendiscord:panel-id","panel","id"))
opendiscord.responders.autocomplete.get("opendiscord:panel-id").workers.add(new api.ODWorker("opendiscord:panel-id",0,async (instance,params,source,cancel) => {
//create panel choices
const panelChoices : {name:string, value:string}[] = []
opendiscord.configs.get("opendiscord:panels").data.forEach((panel) => {
panelChoices.push({name:panel.name, value:panel.id})
})
await instance.filteredAutocomplete(panelChoices)
}))
//OPTION ID AUTOCOMPLETE
opendiscord.responders.autocomplete.add(new api.ODAutocompleteResponder("opendiscord:option-id",/ticket|move/,"id"))
opendiscord.responders.autocomplete.get("opendiscord:option-id").workers.add(new api.ODWorker("opendiscord:option-id",0,async (instance,params,source,cancel) => {
//create ticket choices
const ticketChoices : {name:string, value:string}[] = []
opendiscord.configs.get("opendiscord:options").data.forEach((option) => {
if (option.type != "ticket") return
ticketChoices.push({name:option.name, value:option.id})
})
instance.filteredAutocomplete(ticketChoices)
}))
}
+13
View File
@@ -5,6 +5,7 @@ import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerCommandResponders = async () => {
//CLOSE COMMAND RESPONDER
@@ -64,6 +65,18 @@ export const registerCommandResponders = async () => {
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 (!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()
}
}
const reason = instance.options.getString("reason",false)
+21
View File
@@ -5,6 +5,7 @@ import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerCommandResponders = async () => {
//DELETE COMMAND RESPONDER
@@ -59,10 +60,30 @@ export const registerCommandResponders = async () => {
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 (!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()
}
}
const reason = instance.options.getString("reason",false)
const withoutTranscript = instance.options.getBoolean("notranscript",false) ?? false
//don't allow deleteWithoutTranscript to non-global-admins when enabled
if (withoutTranscript && 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()
}
}
//start deleting ticket
await instance.defer(false)
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:delete-message").build(source,{guild,channel,user,ticket,reason}))
+96
View File
@@ -0,0 +1,96 @@
///////////////////////////////////////
//PRIORITY COMMAND
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerCommandResponders = async () => {
//PRIORITY COMMAND RESPONDER
opendiscord.responders.commands.add(new api.ODCommandResponder("opendiscord:priority",generalConfig.data.prefix,"priority"))
opendiscord.responders.commands.get("opendiscord:priority").workers.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.priority
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(source,{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(source,{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(source,{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(source,{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:priority",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 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()
}
const scope = instance.options.getSubCommand()
if (!scope || (scope != "set" && scope != "get")) return
if (scope == "set"){
const priorityName = instance.options.getString("priority",true)
const reason = instance.options.getString("reason",false)
const priority = opendiscord.priorities.getAll().find((lvl) => lvl.rawName === priorityName) ?? null
if (!priority){
instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build("button",{guild,channel,user,layout:"simple",error:"Please provide a valid priority level.",customTitle:"Unknown Priority Level"}))
return cancel()
}
//start changing ticket priority
await instance.defer(false)
await opendiscord.actions.get("opendiscord:update-ticket-priority").run(source,{guild,channel,user,ticket,newPriority:priority,sendMessage:false,reason})
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:priority-set").build(source,{guild,channel,user,ticket,priority,reason}))
}else if (scope == "get"){
const priority = opendiscord.priorities.getFromPriorityLevel(ticket.get("opendiscord:priority").value)
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:priority-get").build(source,{guild,channel,user,ticket,priority}))
}
}),
new api.ODWorker("opendiscord:logs",-1,(instance,params,source,cancel) => {
const scope = instance.options.getSubCommand()
opendiscord.log(instance.user.displayName+" used the 'priority "+scope+"' command!","info",[
{key:"user",value:instance.user.username},
{key:"userid",value:instance.user.id,hidden:true},
{key:"channelid",value:instance.channel.id,hidden:true},
{key:"method",value:source}
])
})
])
}
+4
View File
@@ -5,6 +5,7 @@ import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
const lang = opendiscord.languages
export const registerCommandResponders = async () => {
//TICKET COMMAND RESPONDER
@@ -70,6 +71,7 @@ export const registerCommandResponders = async () => {
else if (res.reason == "global-user-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"global-user"}))
else if (res.reason == "option-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"option"}))
else if (res.reason == "option-user-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"option-user"}))
else if (res.reason == "custom") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,layout:"simple",error:res.customReason ?? lang.getTranslation("errors.descriptions.unableToCreateTicket")+" `Unknown invalid_permission_reason => no reason specified by plugin`",customTitle:lang.getTranslation("errors.titles.permissionError")}))
else instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Unknown invalid_permission reason => calculation failed #1",layout:"advanced"}))
return cancel()
}
@@ -132,6 +134,7 @@ export const registerButtonResponders = async () => {
else if (res.reason == "global-user-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"global-user"}))
else if (res.reason == "option-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"option"}))
else if (res.reason == "option-user-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"option-user"}))
else if (res.reason == "custom") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,layout:"simple",error:res.customReason ?? lang.getTranslation("errors.descriptions.unableToCreateTicket")+" `Unknown invalid_permission_reason => no reason specified by plugin`",customTitle:lang.getTranslation("errors.titles.permissionError")}))
else instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Unknown invalid_permission reason => calculation failed #1",layout:"advanced"}))
return cancel()
}
@@ -187,6 +190,7 @@ export const registerDropdownResponders = async () => {
else if (res.reason == "global-user-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"global-user"}))
else if (res.reason == "option-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"option"}))
else if (res.reason == "option-user-limit") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error-no-permissions-limits").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,limit:"option-user"}))
else if (res.reason == "custom") instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,layout:"simple",error:res.customReason ?? lang.getTranslation("errors.descriptions.unableToCreateTicket")+" `Unknown invalid_permission_reason => no reason specified by plugin`",customTitle:lang.getTranslation("errors.titles.permissionError")}))
else instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:error").build(source,{guild:instance.guild,channel:instance.channel,user:instance.user,error:"Unknown invalid_permission reason => calculation failed #1",layout:"advanced"}))
return cancel()
}
+83
View File
@@ -0,0 +1,83 @@
///////////////////////////////////////
//TOPIC COMMAND
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerCommandResponders = async () => {
//TOPIC COMMAND RESPONDER
opendiscord.responders.commands.add(new api.ODCommandResponder("opendiscord:topic",generalConfig.data.prefix,"topic"))
opendiscord.responders.commands.get("opendiscord:topic").workers.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.topic
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(source,{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(source,{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(source,{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(source,{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:topic",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 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()
}
const scope = instance.options.getSubCommand()
if (!scope || (scope != "set")) return
if (scope == "set"){
const topic = instance.options.getString("topic",true)
//start changing ticket topic
await instance.defer(false)
await opendiscord.actions.get("opendiscord:update-ticket-topic").run(source,{guild,channel,user,ticket,newTopic:topic,sendMessage:false})
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:topic-set").build(source,{guild,channel,user,ticket,topic}))
}
}),
new api.ODWorker("opendiscord:logs",-1,(instance,params,source,cancel) => {
opendiscord.log(instance.user.displayName+" used the 'topic set' command!","info",[
{key:"user",value:instance.user.username},
{key:"userid",value:instance.user.id,hidden:true},
{key:"channelid",value:instance.channel.id,hidden:true},
{key:"method",value:source}
])
})
])
}
+81
View File
@@ -0,0 +1,81 @@
///////////////////////////////////////
//TRANSFER COMMAND
///////////////////////////////////////
import {opendiscord, api, utilities} from "../index"
import * as discord from "discord.js"
const generalConfig = opendiscord.configs.get("opendiscord:general")
export const registerCommandResponders = async () => {
//TRANSFER COMMAND RESPONDER
opendiscord.responders.commands.add(new api.ODCommandResponder("opendiscord:transfer",generalConfig.data.prefix,"transfer"))
opendiscord.responders.commands.get("opendiscord:transfer").workers.add([
new api.ODWorker("opendiscord:permissions",1,async (instance,params,source,cancel) => {
const permissionMode = generalConfig.data.system.permissions.transfer
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(source,{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(source,{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(source,{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(source,{guild:instance.guild,channel:instance.channel,user:instance.user,permissions:[]}))
return cancel()
}else return
}
}),
new api.ODWorker("opendiscord:transfer",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 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()
}
const oldCreator = await opendiscord.tickets.getTicketUser(ticket,"creator") ?? opendiscord.client.client.user
const newCreator = instance.options.getUser("user",true)
const reason = instance.options.getString("reason",false)
//start transferring ticket ownership
await instance.defer(false)
await opendiscord.actions.get("opendiscord:transfer-ticket").run(source,{guild,channel,user,ticket,reason,sendMessage:false,newCreator})
await instance.reply(await opendiscord.builders.messages.getSafe("opendiscord:transfer-message").build(source,{guild,channel,user,ticket,oldCreator,newCreator,reason}))
}),
new api.ODWorker("opendiscord:logs",-1,(instance,params,source,cancel) => {
opendiscord.log(instance.user.displayName+" used the 'transfer' command!","info",[
{key:"user",value:instance.user.username},
{key:"userid",value:instance.user.id,hidden:true},
{key:"channelid",value:instance.channel.id,hidden:true},
{key:"method",value:source}
])
})
])
}
+1
View File
@@ -61,3 +61,4 @@ export * from "./openticket/ticket"
export * from "./openticket/blacklist"
export * from "./openticket/transcript"
export * from "./openticket/role"
export * from "./openticket/priority"
+21 -2
View File
@@ -10,6 +10,7 @@ import { ODTicket, ODTicketClearFilter } from "../openticket/ticket"
import { ODTranscriptCompiler, ODTranscriptCompilerCompileResult } from "../openticket/transcript"
import { ODMessageBuildSentResult } from "../modules/builder"
import { ODRole, ODRoleUpdateMode, ODRoleUpdateResult } from "../openticket/role"
import { ODPriorityLevel } from "../openticket/priority"
/**## ODActionManagerIds_Default `interface`
* This interface is a list of ids available in the `ODActionManager_Default` class.
@@ -19,7 +20,7 @@ export interface ODActionManagerIds_Default {
"opendiscord:create-ticket-permissions":{
source:"panel-button"|"panel-dropdown"|"slash"|"text"|"other",
params:{guild:discord.Guild,user:discord.User,option:ODTicketOption},
result:{valid:boolean,reason:"blacklist"|"cooldown"|"global-limit"|"global-user-limit"|"option-limit"|"option-user-limit"|null,cooldownUntil?:Date},
result:{valid:boolean,reason:"blacklist"|"cooldown"|"global-limit"|"global-user-limit"|"option-limit"|"option-user-limit"|"custom"|null,cooldownUntil?:Date,customReason?:string},
workers:"opendiscord:check-blacklist"|"opendiscord:check-cooldown"|"opendiscord:check-global-limits"|"opendiscord:check-option-limits"|"opendiscord:valid"
},
"opendiscord:create-transcript":{
@@ -111,7 +112,25 @@ export interface ODActionManagerIds_Default {
params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:ODTicket[]},
result:{list:string[]},
workers:"opendiscord:clear-tickets"|"opendiscord:discord-logs"|"opendiscord:logs"
}
},
"opendiscord:update-ticket-topic":{
source:"slash"|"text"|"ticket-action"|"other",
params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,newTopic:string|null,sendMessage:boolean},
result:{},
workers:"opendiscord:update-ticket-topic"|"opendiscord:discord-logs"|"opendiscord:logs"
},
"opendiscord:update-ticket-priority":{
source:"slash"|"text"|"other",
params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,newPriority:ODPriorityLevel,reason:string|null,sendMessage:boolean},
result:{},
workers:"opendiscord:update-ticket-priority"|"opendiscord:discord-logs"|"opendiscord:logs"
},
"opendiscord:transfer-ticket":{
source:"slash"|"text"|"other",
params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,newCreator:discord.User,reason:string|null,sendMessage:boolean},
result:{},
workers:"opendiscord:transfer-ticket"|"opendiscord:discord-logs"|"opendiscord:logs"
},
}
/**## ODActionManager_Default `default_class`
+21 -4
View File
@@ -13,6 +13,7 @@ import { ODVerifyBar } from "../modules/verifybar"
import * as discord from "discord.js"
import { ODTranscriptCompiler, ODTranscriptCompilerCompileResult } from "../openticket/transcript"
import { ODRole, ODRoleUpdateResult } from "../openticket/role"
import { ODPriorityLevel } from "../openticket/priority"
/**## ODBuilderManager_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODBuilderManager class.
@@ -224,7 +225,7 @@ export class ODFile_Default<Source extends string, Params, WorkerIds extends str
* It's used to generate typescript declarations for this class.
*/
export interface ODEmbedManagerIds_Default {
"opendiscord:error":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:string,layout:"simple"|"advanced"},workers:"opendiscord:error"},
"opendiscord:error":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:string,layout:"simple"|"advanced",customTitle?:string},workers:"opendiscord:error"},
"opendiscord:error-option-missing":{source:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:ODTextCommandErrorMissingOption},workers:"opendiscord:error-option-missing"},
"opendiscord:error-option-invalid":{source:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:ODTextCommandErrorInvalidOption},workers:"opendiscord:error-option-invalid"},
"opendiscord:error-unknown-command":{source:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:ODTextCommandErrorUnknownCommand},workers:"opendiscord:error-unknown-command"},
@@ -238,7 +239,7 @@ export interface ODEmbedManagerIds_Default {
"opendiscord:error-option-unknown":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-option-unknown"},
"opendiscord:error-panel-unknown":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-panel-unknown"},
"opendiscord:error-not-in-guild":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-not-in-guild"},
"opendiscord:error-channel-rename":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"ticket-pin"|"ticket-unpin"|"ticket-rename"|"ticket-move"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,originalName:string,newName:string},workers:"opendiscord:error-channel-rename"},
"opendiscord:error-channel-rename":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"ticket-pin"|"ticket-unpin"|"ticket-rename"|"ticket-move"|"ticket-priority"|"ticket-transfer"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,originalName:string,newName:string},workers:"opendiscord:error-channel-rename"},
"opendiscord:error-ticket-busy":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-ticket-busy"},
"opendiscord:help-menu":{source:"text"|"slash"|"button"|"other",params:{mode:"slash"|"text",page:number},workers:"opendiscord:help-menu"},
@@ -281,6 +282,9 @@ export interface ODEmbedManagerIds_Default {
"opendiscord:transcript-error":{source:"slash"|"text"|"ticket-message"|"reopen-message"|"close-message"|"autoclose-message"|"autodelete"|"clear"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,compiler:ODTranscriptCompiler<any,object|null>,reason:string|null},workers:"opendiscord:transcript-error"},
"opendiscord:reaction-role":{source:"panel-button"|"other",params:{guild:discord.Guild,user:discord.User,role:ODRole,result:ODRoleUpdateResult[]},workers:"opendiscord:reaction-role"},
"opendiscord:reaction-role-dm":{source:"panel-button"|"other",params:{guild:discord.Guild,user:discord.User,role:ODRole,result:ODRoleUpdateResult[]},workers:"opendiscord:reaction-role-dm"},
"opendiscord:reaction-role-logs":{source:"panel-button"|"other",params:{guild:discord.Guild,user:discord.User,role:ODRole,result:ODRoleUpdateResult[]},workers:"opendiscord:reaction-role-logs"},
"opendiscord:clear-verify-message":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:string[]},workers:"opendiscord:clear-verify-message"},
"opendiscord:clear-message":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:string[]},workers:"opendiscord:clear-message"},
"opendiscord:clear-logs":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:string[]},workers:"opendiscord:clear-logs"},
@@ -291,6 +295,11 @@ export interface ODEmbedManagerIds_Default {
"opendiscord:autodelete-enable":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,time:number,reason:string|null},workers:"opendiscord:autodelete-enable"},
"opendiscord:autoclose-disable":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,reason:string|null},workers:"opendiscord:autoclose-disable"},
"opendiscord:autodelete-disable":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,reason:string|null},workers:"opendiscord:autodelete-disable"},
"opendiscord:topic-set":{source:"slash"|"text"|"ticket-action"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,topic:string},workers:"opendiscord:topic-set"},
"opendiscord:priority-set":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,priority:ODPriorityLevel,reason:string|null},workers:"opendiscord:priority-set"},
"opendiscord:priority-get":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,priority:ODPriorityLevel},workers:"opendiscord:priority-get"},
"opendiscord:transfer-message":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,oldCreator:discord.User,newCreator:discord.User,reason:string|null},workers:"opendiscord:transfer-message"},
}
/**## ODEmbedManager_Default `default_class`
@@ -353,7 +362,7 @@ export interface ODMessageManagerIds_Default {
"opendiscord:verifybar-unpin-message":{source:"verifybar",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,verifybar:ODVerifyBar,originalMessage:discord.Message<boolean>},workers:"opendiscord:verifybar-unpin-message"}
"opendiscord:verifybar-autoclose-message":{source:"verifybar",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,verifybar:ODVerifyBar,originalMessage:discord.Message<boolean>},workers:"opendiscord:verifybar-autoclose-message"}
"opendiscord:error":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:string,layout:"simple"|"advanced"},workers:"opendiscord:error"},
"opendiscord:error":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:string,layout:"simple"|"advanced",customTitle?:string},workers:"opendiscord:error"},
"opendiscord:error-option-missing":{source:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:ODTextCommandErrorMissingOption},workers:"opendiscord:error-option-missing"},
"opendiscord:error-option-invalid":{source:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:ODTextCommandErrorInvalidOption},workers:"opendiscord:error-option-invalid"},
"opendiscord:error-unknown-command":{source:"slash"|"text"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,error:ODTextCommandErrorUnknownCommand},workers:"opendiscord:error-unknown-command"},
@@ -367,7 +376,7 @@ export interface ODMessageManagerIds_Default {
"opendiscord:error-option-unknown":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-option-unknown"},
"opendiscord:error-panel-unknown":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-panel-unknown"},
"opendiscord:error-not-in-guild":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-not-in-guild"},
"opendiscord:error-channel-rename":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"ticket-pin"|"ticket-unpin"|"ticket-rename"|"ticket-move"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,originalName:string,newName:string},workers:"opendiscord:error-channel-rename"},
"opendiscord:error-channel-rename":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"ticket-pin"|"ticket-unpin"|"ticket-rename"|"ticket-move"|"ticket-priority"|"ticket-transfer"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User,originalName:string,newName:string},workers:"opendiscord:error-channel-rename"},
"opendiscord:error-ticket-busy":{source:"slash"|"text"|"button"|"dropdown"|"modal"|"other",params:{guild:discord.Guild|null,channel:discord.TextBasedChannel,user:discord.User},workers:"opendiscord:error-ticket-busy"},
"opendiscord:help-menu":{source:"slash"|"text"|"button"|"other",params:{mode:"slash"|"text",page:number},workers:"opendiscord:help-menu"},
@@ -412,6 +421,9 @@ export interface ODMessageManagerIds_Default {
"opendiscord:transcript-error":{source:"slash"|"text"|"ticket-message"|"reopen-message"|"close-message"|"autoclose-message"|"autodelete"|"clear"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,compiler:ODTranscriptCompiler<any,object|null>,reason:string|null},workers:"opendiscord:transcript-error"},
"opendiscord:reaction-role":{source:"panel-button"|"other",params:{guild:discord.Guild,user:discord.User,role:ODRole,result:ODRoleUpdateResult[]},workers:"opendiscord:reaction-role"},
"opendiscord:reaction-role-dm":{source:"panel-button"|"other",params:{guild:discord.Guild,user:discord.User,role:ODRole,result:ODRoleUpdateResult[]},workers:"opendiscord:reaction-role-dm"},
"opendiscord:reaction-role-logs":{source:"panel-button"|"other",params:{guild:discord.Guild,user:discord.User,role:ODRole,result:ODRoleUpdateResult[]},workers:"opendiscord:reaction-role-logs"},
"opendiscord:clear-verify-message":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:string[]},workers:"opendiscord:clear-verify-message"},
"opendiscord:clear-message":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:string[]},workers:"opendiscord:clear-message"},
"opendiscord:clear-logs":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,filter:ODTicketClearFilter,list:string[]},workers:"opendiscord:clear-logs"},
@@ -422,6 +434,11 @@ export interface ODMessageManagerIds_Default {
"opendiscord:autodelete-enable":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,time:number,reason:string|null},workers:"opendiscord:autodelete-enable"},
"opendiscord:autoclose-disable":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,reason:string|null},workers:"opendiscord:autoclose-disable"},
"opendiscord:autodelete-disable":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,reason:string|null},workers:"opendiscord:autodelete-disable"},
"opendiscord:topic-set":{source:"slash"|"text"|"ticket-action"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,topic:string},workers:"opendiscord:topic-set"},
"opendiscord:priority-set":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,priority:ODPriorityLevel,reason:string|null},workers:"opendiscord:priority-set"},
"opendiscord:priority-get":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,priority:ODPriorityLevel},workers:"opendiscord:priority-get"},
"opendiscord:transfer-message":{source:"slash"|"text"|"other",params:{guild:discord.Guild,channel:discord.GuildTextBasedChannel,user:discord.User,ticket:ODTicket,oldCreator:discord.User,newCreator:discord.User,reason:string|null},workers:"opendiscord:transfer-message"},
}
/**## ODMessageManager_Default `default_class`
+12 -8
View File
@@ -183,14 +183,8 @@ export class ODCheckerRenderer_Default extends ODCheckerRenderer {
return finalComponents
}
/**Get the length of the longest string in the array. */
#getLongestLength(text:string[]): number {
let finalLength = 0
text.forEach((t) => {
const l = ansis.strip(t).length
if (l > finalLength) finalLength = l
})
return finalLength
#getLongestLength(texts:string[]): number {
return Math.max(...texts.map((t) => ansis.strip(t).length))
}
/**Get a horizontal divider used between different parts of the config checker result. */
#getHorizontalDivider(width:number): string {
@@ -247,9 +241,18 @@ export type ODCheckerTranslationRegisterMessageIds_Default = (
"opendiscord:string-starts-with"|
"opendiscord:string-ends-with"|
"opendiscord:string-contains"|
"opendiscord:string-inverted-contains"|
"opendiscord:string-choices"|
"opendiscord:string-lowercase"|
"opendiscord:string-uppercase"|
"opendiscord:string-special-characters"|
"opendiscord:string-no-spaces"|
"opendiscord:string-regex"|
"opendiscord:string-capital-word"|
"opendiscord:string-capital-sentence"|
"opendiscord:string-punctuation"|
"opendiscord:number-nan"|
"opendiscord:number-too-short"|
"opendiscord:number-too-long"|
"opendiscord:number-length-invalid"|
@@ -261,6 +264,7 @@ export type ODCheckerTranslationRegisterMessageIds_Default = (
"opendiscord:number-starts-with"|
"opendiscord:number-ends-with"|
"opendiscord:number-contains"|
"opendiscord:number-inverted-contains"|
"opendiscord:number-choices"|
"opendiscord:number-float"|
"opendiscord:number-negative"|
+64 -2
View File
@@ -2,7 +2,17 @@
//DEFAULT CLIENT MODULE
///////////////////////////////////////
import { ODValidId } from "../modules/base"
import { ODClientManager, ODSlashCommand, ODTextCommand, ODSlashCommandManager, ODTextCommandManager, ODSlashCommandInteractionCallback, ODTextCommandInteractionCallback } from "../modules/client"
import { ODClientManager, ODSlashCommand, ODTextCommand, ODSlashCommandManager, ODTextCommandManager, ODSlashCommandInteractionCallback, ODTextCommandInteractionCallback, ODContextMenu, ODContextMenuManager, ODContextMenuInteractionCallback } from "../modules/client"
/** (CONTRIBUTOR GUIDE) HOW TO ADD NEW COMMANDS?
* - Register the command in loadAllSlashCommands() & loadAllTextCommands() in (./src/data/framework/commandLoader.ts)
* - Add autocomplete for the command in OD(Slash/Text)CommandManagerIds_Default in (./src/core/api/defaults/client.ts)
* - Add the command to the help menu in (./src/data/framework/helpMenuLoader.ts)
* - If required, new config variables should be added (incl. logs, dm-logs & permissions).
* - Update the Open Ticket Documentation.
* - If the command contains complex logic or can be executed from a button/dropdown, it should be placed inside an `ODAction`.
* - Check all files, test the bot carefully & try a lot of different scenario's with different settings.
*/
/**## ODClientManager_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODClientManager class.
@@ -13,6 +23,7 @@ import { ODClientManager, ODSlashCommand, ODTextCommand, ODSlashCommandManager,
export class ODClientManager_Default extends ODClientManager {
declare slashCommands: ODSlashCommandManager_Default
declare textCommands: ODTextCommandManager_Default
declare contextMenus: ODContextMenuManager_Default
}
/**## ODSlashCommandManagerIds_Default `interface`
@@ -39,6 +50,9 @@ export interface ODSlashCommandManagerIds_Default {
"opendiscord:clear":ODSlashCommand,
"opendiscord:autoclose":ODSlashCommand,
"opendiscord:autodelete":ODSlashCommand,
"opendiscord:topic":ODSlashCommand,
"opendiscord:priority":ODSlashCommand,
"opendiscord:transfer":ODSlashCommand,
}
/**## ODSlashCommandManager_Default `default_class`
@@ -108,7 +122,11 @@ export interface ODTextCommandManagerIds_Default {
"opendiscord:autoclose-disable":ODTextCommand,
"opendiscord:autoclose-enable":ODTextCommand,
"opendiscord:autodelete-disable":ODTextCommand,
"opendiscord:autodelete-enable":ODTextCommand
"opendiscord:autodelete-enable":ODTextCommand,
"opendiscord:topic-set":ODTextCommand,
"opendiscord:priority-set":ODTextCommand,
"opendiscord:priority-get":ODTextCommand,
"opendiscord:transfer":ODTextCommand,
}
/**## ODTextCommandManager_Default `default_class`
@@ -143,3 +161,47 @@ export class ODTextCommandManager_Default extends ODTextCommandManager {
return super.onInteraction(commandPrefix,commandName,callback)
}
}
/**## ODContextMenuManagerIds_Default `interface`
* This interface is a list of ids available in the `ODContextMenuManager_Default` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODContextMenuManagerIds_Default {
//"opendiscord:test-menu":ODContextMenu
}
/**## ODContextMenuManager_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODContextMenuManager class.
* It doesn't add any extra features!
*
* This default class is made for the global variable `opendiscord.client.contextMenus`!
*/
export class ODContextMenuManager_Default extends ODContextMenuManager {
get<ContextMenuId extends keyof ODContextMenuManagerIds_Default>(id:ContextMenuId): ODContextMenuManagerIds_Default[ContextMenuId]
get(id:ODValidId): ODContextMenu|null
get(id:ODValidId): ODContextMenu|null {
return super.get(id)
}
remove<ContextMenuId extends keyof ODContextMenuManagerIds_Default>(id:ContextMenuId): ODContextMenuManagerIds_Default[ContextMenuId]
remove(id:ODValidId): ODContextMenu|null
remove(id:ODValidId): ODContextMenu|null {
return super.remove(id)
}
exists(id:keyof ODContextMenuManagerIds_Default): boolean
exists(id:ODValidId): boolean
exists(id:ODValidId): boolean {
return super.exists(id)
}
onInteraction(menuName:keyof ODContextMenuManagerIds_Default, callback:ODContextMenuInteractionCallback): void
onInteraction(menuName:string|RegExp, callback:ODContextMenuInteractionCallback): void
onInteraction(menuName:string|RegExp, callback:ODContextMenuInteractionCallback): void {
return super.onInteraction(menuName,callback)
}
}
+140 -42
View File
@@ -4,18 +4,38 @@
import { ODValidButtonColor, ODValidId } from "../modules/base"
import * as discord from "discord.js"
import { ODConfigManager, ODConfig, ODJsonConfig } from "../modules/config"
import { ODClientActivityStatus, ODClientActivityType } from "../modules/client"
import { ODClientActivityMode, ODClientActivityType } from "../modules/client"
import { ODRoleUpdateMode } from "../openticket/role"
/** (CONTRIBUTOR GUIDE) HOW TO ADD NEW CONFIG VARIABLES?
* - Make the change to the config file in (./config/) and be aware of the following things:
* - The variable has a clear name and its function is obvious.
* - The variable is in the correct position/category of the config.
* - The variable contains a default placeholder to suggest the contents.
* - If there's a (./devconfig/), also modify this file.
* - Register the config in loadAllConfigs() in (./src/data/framework/configLoader.ts)
* - The variable should be added to the "formatters" in the correct position.
* - Add autocomplete for the variable in ODJsonConfig_Default... in (./src/core/api/defaults/config.ts)
* - Add the variable to the config checker in (./src/data/framework/checkerLoader.ts)
* - Make sure the variable is compatible with the Interactive Setup CLI.
* - The variable should be added by the migration manager (./src/core/startup/migration.ts) when missing.
* - Update the Open Ticket Documentation.
*
* IF VARIABLE IS FROM questions.json, options.json OR panels.json:
* - Check (./src/data/openticket/...) for loading/unloading of data.
* - Check (./src/actions/createTicket.ts) and related files.
* - Check (./src/builders), (./src/actions), (./src/data) & (./src/commands) in general in the areas that were changed.
*/
/**## ODConfigManagerIds_Default `interface`
* This interface is a list of ids available in the `ODConfigManager_Default` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODConfigManagerIds_Default {
"opendiscord:general":ODJsonConfig_DefaultGeneral,
"opendiscord:questions":ODJsonConfig_DefaultQuestions,
"opendiscord:options":ODJsonConfig_DefaultOptions,
"opendiscord:panels":ODJsonConfig_DefaultPanels,
"opendiscord:questions":ODJsonConfig_DefaultQuestions,
"opendiscord:transcripts":ODJsonConfig_DefaultTranscripts
}
@@ -56,10 +76,12 @@ export interface ODJsonConfig_DefaultStatusType {
enabled:boolean,
/**The type of status (e.g. playing, listening, custom, ...) */
type:Exclude<ODClientActivityType,false>,
/**The mode/status of the bot (e.g. online, invisible, idle, do not disturb) */
mode:ODClientActivityMode
/**The text for the status. */
text:string,
/**The status of the bot (e.g. online, invisible, idle, do not disturb) */
status:ODClientActivityStatus
/**Additional text for the status. (visible below 'text') */
state:string,
}
/**## ODJsonConfig_DefaultMessageSettingsType `interface`
@@ -111,6 +133,30 @@ export interface ODJsonConfig_DefaultSystemLimits {
userMaximum:number
}
/**## ODJsonConfig_DefaultSystemChannelTopic `interface`
* All global channel topic settings.
*/
export interface ODJsonConfig_DefaultSystemChannelTopic {
/**Show the option name in the channel topic. */
showOptionName:boolean,
/**Show the option description in the channel topic. */
showOptionDescription:boolean,
/**Show the option topic text in the channel topic (configured in the options config). */
showOptionTopic:boolean,
/**Show the current priority in the channel topic (auto-updated). */
showPriority:boolean,
/**Show the current close/reopen status in the channel topic (auto-updated). */
showClosed:boolean,
/**Show the current claim status in the channel topic (auto-updated). */
showClaimed:boolean,
/**Show the current pin status in the channel topic (auto-updated). */
showPinned:boolean,
/**Show the creator of the ticket in the channel topic (auto-updated on transfer). */
showCreator:boolean,
/**Show the first 5 participants of the ticket in the channel topic (auto-updated). */
showParticipants:boolean
}
/**## ODJsonConfig_DefaultSystemPermissions `interface`
* Configure permissions for all Open Ticket commands & actions.
*/
@@ -133,7 +179,10 @@ export interface ODJsonConfig_DefaultSystemPermissions {
stats:ODJsonConfig_DefaultCmdPermissionSettingsType,
clear:ODJsonConfig_DefaultCmdPermissionSettingsType,
autoclose:ODJsonConfig_DefaultCmdPermissionSettingsType,
autodelete:ODJsonConfig_DefaultCmdPermissionSettingsType
autodelete:ODJsonConfig_DefaultCmdPermissionSettingsType,
transfer:ODJsonConfig_DefaultCmdPermissionSettingsType,
topic:ODJsonConfig_DefaultCmdPermissionSettingsType,
priority:ODJsonConfig_DefaultCmdPermissionSettingsType,
}
/**## ODJsonConfig_DefaultSystemMessages `interface`
@@ -151,34 +200,59 @@ export interface ODJsonConfig_DefaultSystemMessages {
renaming:ODJsonConfig_DefaultMessageSettingsType,
moving:ODJsonConfig_DefaultMessageSettingsType,
blacklisting:ODJsonConfig_DefaultMessageSettingsType,
roleAdding:ODJsonConfig_DefaultMessageSettingsType,
roleRemoving:ODJsonConfig_DefaultMessageSettingsType
transferring:ODJsonConfig_DefaultMessageSettingsType,
topicChange:ODJsonConfig_DefaultMessageSettingsType,
priorityChange:ODJsonConfig_DefaultMessageSettingsType,
reactionRole:ODJsonConfig_DefaultMessageSettingsType,
}
/**## ODJsonConfig_DefaultSystem `interface`
* All settings related to the ticket system.
*/
export interface ODJsonConfig_DefaultSystem {
/**Remove all participants (except admins) from the ticket when it's closed. */
removeParticipantsOnClose:boolean,
/**Reply with an ephemeral message when a ticket is created. */
replyOnTicketCreation:boolean,
/**Reply with an ephemeral message when reaction roles are changed. */
replyOnReactionRole:boolean,
/**Use a translated config checker in the console. */
useTranslatedConfigChecker:boolean,
/**Prefer slash-commands over text-commands when displaying them in menu's and messages. */
preferSlashOverText:boolean,
/**Reply with "unknown command" when the prefix is used without a valid command. */
sendErrorOnUnknownCommand:boolean,
/**Display the question fields (in a ticket message) in code blocks. */
questionFieldsInCodeBlock:boolean,
/**Display embed fields together with question fields (in a ticket message). */
displayFieldsWithQuestions:boolean,
/**Show global admins roles together with ticket admins in panel embeds. */
showGlobalAdminsInPanelRoles:boolean,
/**Disable the (✅❌) buttons and directly run the action. */
disableVerifyBars:boolean,
/**Display error embeds/messages with red instead of the default bot color. */
useRedErrorEmbeds:boolean,
/**Always show the reason field in embeds, even when there is no reason provided. */
alwaysShowReason:boolean,
/**The emoji style used in the bot. This will affect all embeds, titles & messages in the bot. */
emojiStyle:"before"|"after"|"double"|"disabled",
/**The emoji used when pinning tickets. This is '📌' by default. */
pinEmoji:string,
/**Reply with an ephemeral message when a ticket is created. */
replyOnTicketCreation:boolean,
/**Reply with an ephemeral message when reaction roles are changed. */
replyOnReactionRole:boolean,
/**Ask for the priority of this ticket on ticket creation. This will happen in a dropdown in the ticket message. */
askPriorityOnTicketCreation:boolean,
/**Remove all participants (except admins) from the ticket when it's closed. */
removeParticipantsOnClose:boolean,
/**Disable autoclose for a ticket when it has been closed and re-opened. */
disableAutocloseAfterReopen:boolean,
/**Only allow autodelete when the ticket is already closed. */
autodeleteRequiresClosedTicket:boolean,
/**When enabled, only global admins are able to delete a ticket without transcript. */
adminOnlyDeleteWithoutTranscript:boolean,
/**Only allow ticket closing when at least 1 message has been sent by the creator. (admins are able to bypass) */
allowCloseBeforeMessage:boolean,
/**Only allow ticket closing when at least 1 message has been sent by a global or ticket admin. (admins are able to bypass) */
allowCloseBeforeAdminMessage:boolean,
/**Use a translated config checker in the console. */
useTranslatedConfigChecker:boolean,
/**Pin the (first) ticket message in the channel. This simulates old behaviour like Open Ticket v1, v2 & v3. */
pinFirstTicketMessage:boolean,
/**Enable/disable the ticket claim & unclaim button in the ticket message. */
enableTicketClaimButtons:boolean,
@@ -199,6 +273,9 @@ export interface ODJsonConfig_DefaultSystem {
/**All settings related to global ticket limits. */
limits:ODJsonConfig_DefaultSystemLimits,
/**All global channel topic settings. */
channelTopic:ODJsonConfig_DefaultSystemChannelTopic,
/**Configure permissions for all Open Ticket commands & actions. */
permissions:ODJsonConfig_DefaultSystemPermissions,
@@ -219,7 +296,7 @@ export interface ODJsonConfig_DefaultGeneralData {
tokenFromENV:boolean,
/**The main (hex) color used in almost every embed in the bot. */
mainColor:discord.ColorResolvable,
mainColor:discord.ColorResolvable|string,
/**The language to use. Can be the id of the language or the id without the prefix when using `opendiscord:...`. */
language:string,
/**The prefix used in all text-commands. */
@@ -295,7 +372,7 @@ export interface ODJsonConfig_DefaultOptionEmbedSettingsType {
/**The description of this embed. */
description:string,
/**A custom color for this embed. (The default bot color is used when empty) */
customColor:discord.ColorResolvable,
customColor:discord.ColorResolvable|string,
/**A URL to an image displayed in the embed. */
image:string,
@@ -332,7 +409,7 @@ export interface ODJsonConfig_DefaultOptionTicketChannelType {
/**The prefix used in the name of this ticket channel. */
prefix:string,
/**The type of suffix used in the name of this ticket channel. */
suffix:"user-name"|"user-id"|"random-number"|"random-hex"|"counter-dynamic"|"counter-fixed",
suffix:"user-name"|"user-nickname"|"user-id"|"random-number"|"random-hex"|"counter-dynamic"|"counter-fixed",
/**An optional discord category id to create this ticket in. */
category:string,
/**An optional discord category id to move this ticket to when closed. */
@@ -346,8 +423,8 @@ export interface ODJsonConfig_DefaultOptionTicketChannelType {
/**The category to move the ticket to when claimed by this user. */
category:string
}[],
/**The channel description/topic shown at the top of the channel in discord. */
description:string
/**The channel topic shown at the top of the channel in discord. */
topic:string
}
/**## ODJsonConfig_DefaultOptionTicketType `interface`
@@ -408,14 +485,14 @@ export interface ODJsonConfig_DefaultOptionTicketType extends ODJsonConfig_Defau
/**Disable autodeleting when the ticket is claimed by someone. */
disableOnClaim:boolean
},
/**All settings related to the cooldown of this ticket type */
/**All settings related to the cooldown of this ticket type. */
cooldown:{
/**Enable cooldown (per user) */
enabled:boolean,
/**The amount of minutes a user needs to wait before being able to create a ticket again. */
cooldownMinutes:number
},
/**All settings related to the limits of this ticket type */
/**All settings related to the limits of this ticket type. */
limits:{
/**Enable option ticket limits. */
enabled:boolean,
@@ -423,6 +500,13 @@ export interface ODJsonConfig_DefaultOptionTicketType extends ODJsonConfig_Defau
globalMaximum:number,
/**The maximum amount of tickets of this type that a user is allowed to create at the same time. */
userMaximum:number
},
/**All settings related to the slow mode of this ticket type. */
slowMode:{
/**Enable channel slow mode. */
enabled:boolean,
/**The amount of seconds users need to wait between sending messages. */
slowModeSeconds:number
}
}
@@ -451,6 +535,11 @@ export interface ODJsonConfig_DefaultOptionRoleType extends ODJsonConfig_Default
addOnMemberJoin:boolean
}
/**## ODJsonConfig_DefaultOptionsData `type`
* All contents of the `options.json` config file.
*/
export type ODJsonConfig_DefaultOptionsData = (ODJsonConfig_DefaultOptionTicketType|ODJsonConfig_DefaultOptionWebsiteType|ODJsonConfig_DefaultOptionRoleType)[]
/**## ODJsonConfig_DefaultOptions `default_class`
* This is a special class that adds type definitions & typescript to the ODJsonConfig class.
* It doesn't add any extra features!
@@ -458,11 +547,7 @@ export interface ODJsonConfig_DefaultOptionRoleType extends ODJsonConfig_Default
* This default class is made for the `options.json` config!
*/
export class ODJsonConfig_DefaultOptions extends ODJsonConfig {
declare data: (
ODJsonConfig_DefaultOptionTicketType|
ODJsonConfig_DefaultOptionWebsiteType|
ODJsonConfig_DefaultOptionRoleType
)[]
declare data: ODJsonConfig_DefaultOptionsData
}
/**## ODJsonConfig_DefaultPanelEmbedSettingsType `interface`
@@ -477,7 +562,7 @@ export interface ODJsonConfig_DefaultPanelEmbedSettingsType {
description:string,
/**A custom color for this embed. (The default bot color is used when empty) */
customColor:discord.ColorResolvable,
customColor:discord.ColorResolvable|string,
/**An optional URL used in the title of the embed. */
url:string,
@@ -545,6 +630,11 @@ export interface ODJsonConfig_DefaultPanelType {
settings:ODJsonConfig_DefaultPanelSettingsType
}
/**## ODJsonConfig_DefaultPanelsData `type`
* All contents of the `panels.json` config file.
*/
export type ODJsonConfig_DefaultPanelsData = ODJsonConfig_DefaultPanelType[]
/**## ODJsonConfig_DefaultPanels `default_class`
* This is a special class that adds type definitions & typescript to the ODJsonConfig class.
* It doesn't add any extra features!
@@ -552,7 +642,7 @@ export interface ODJsonConfig_DefaultPanelType {
* This default class is made for the `panels.json` config!
*/
export class ODJsonConfig_DefaultPanels extends ODJsonConfig {
declare data: ODJsonConfig_DefaultPanelType[]
declare data: ODJsonConfig_DefaultPanelsData
}
/**## ODJSonConfig_DefaultQuestionLengthSettings `interface`
@@ -605,6 +695,11 @@ export interface ODJsonConfig_DefaultParagraphQuestionType {
length:ODJSonConfig_DefaultQuestionLengthSettings
}
/**## ODJsonConfig_DefaultQuestionsData `type`
* All contents of the `questions.json` config file.
*/
export type ODJsonConfig_DefaultQuestionsData = (ODJsonConfig_DefaultShortQuestionType|ODJsonConfig_DefaultParagraphQuestionType)[]
/**## ODJsonConfig_DefaultQuestions `default_class`
* This is a special class that adds type definitions & typescript to the ODJsonConfig class.
* It doesn't add any extra features!
@@ -612,10 +707,7 @@ export interface ODJsonConfig_DefaultParagraphQuestionType {
* This default class is made for the `questions.json` config!
*/
export class ODJsonConfig_DefaultQuestions extends ODJsonConfig {
declare data: (
ODJsonConfig_DefaultShortQuestionType|
ODJsonConfig_DefaultParagraphQuestionType
)[]
declare data: ODJsonConfig_DefaultQuestionsData
}
/**## ODJsonConfig_DefaultTranscriptsTextLayout `interface`
@@ -689,14 +781,10 @@ export interface ODJsonConfig_DefaultTranscriptsHtmlLayout {
}
}
/**## ODJsonConfig_DefaultTranscripts `default_class`
* This is a special class that adds type definitions & typescript to the ODJsonConfig class.
* It doesn't add any extra features!
*
* This default class is made for the `transcripts.json` config!
/**## ODJsonConfig_DefaultTranscriptsData `interface`
* All contents of the `transcripts.json` config file.
*/
export class ODJsonConfig_DefaultTranscripts extends ODJsonConfig {
declare data: {
export interface ODJsonConfig_DefaultTranscriptsData {
/**All general settings related to transcripts. */
general:{
/**Are transcripts enabled? */
@@ -721,7 +809,7 @@ export class ODJsonConfig_DefaultTranscripts extends ODJsonConfig {
/**All settings related to the embed from the transcripts. (UNIMPLEMENTED!!) */
embedSettings:{
/**Unimplemented feature */
customColor:discord.ColorResolvable,
customColor:discord.ColorResolvable|string,
/**Unimplemented feature */
listAllParticipants:boolean,
/**Unimplemented feature */
@@ -731,5 +819,15 @@ export class ODJsonConfig_DefaultTranscripts extends ODJsonConfig {
textTranscriptStyle:ODJsonConfig_DefaultTranscriptsTextLayout,
/**The layout of the HTML transcripts. */
htmlTranscriptStyle:ODJsonConfig_DefaultTranscriptsHtmlLayout
}
}
/**## ODJsonConfig_DefaultTranscripts `default_class`
* This is a special class that adds type definitions & typescript to the ODJsonConfig class.
* It doesn't add any extra features!
*
* This default class is made for the `transcripts.json` config!
*/
export class ODJsonConfig_DefaultTranscripts extends ODJsonConfig {
declare data: ODJsonConfig_DefaultTranscriptsData
}
+23 -2
View File
@@ -19,9 +19,9 @@ import { ODFlagManager_Default } from "./flag"
import { ODSessionManager_Default } from "./session"
import { ODLanguageManager_Default } from "./language"
import { ODCheckerFunctionManager_Default, ODCheckerManager_Default, ODCheckerRenderer_Default, ODCheckerTranslationRegister_Default } from "./checker"
import { ODClientManager_Default, ODSlashCommandManager_Default, ODTextCommandManager_Default } from "./client"
import { ODClientManager_Default, ODContextMenuManager_Default, ODSlashCommandManager_Default, ODTextCommandManager_Default } from "./client"
import { ODBuilderManager_Default, ODButtonManager_Default, ODDropdownManager_Default, ODEmbedManager_Default, ODFileManager_Default, ODMessageManager_Default, ODModalManager_Default } from "./builder"
import { ODButtonResponderManager_Default, ODCommandResponderManager_Default, ODDropdownResponderManager_Default, ODModalResponderManager_Default, ODResponderManager_Default } from "./responder"
import { ODAutocompleteResponderManager_Default, ODButtonResponderManager_Default, ODCommandResponderManager_Default, ODContextMenuResponderManager_Default, ODDropdownResponderManager_Default, ODModalResponderManager_Default, ODResponderManager_Default } from "./responder"
import { ODActionManager_Default } from "./action"
import { ODPermissionManager_Default } from "./permission"
import { ODHelpMenuManager_Default } from "./helpmenu"
@@ -42,6 +42,7 @@ import { ODQuestionManager } from "../openticket/question"
import { ODBlacklistManager } from "../openticket/blacklist"
import { ODTranscriptManager_Default } from "../openticket/transcript"
import { ODRole, ODRoleManager } from "../openticket/role"
import { ODPriorityLevel, ODPriorityManager_Default } from "../openticket/priority"
/**## ODEventIds_Default `interface`
* This interface is a list of ids available in the `ODEvent_Default` class.
@@ -122,12 +123,22 @@ export interface ODEventIds_Default {
"onClientActivityInit": ODEvent_Default<(activity:ODClientActivityManager, client:ODClientManager_Default) => ODPromiseVoid>
"afterClientActivityInitiated": ODEvent_Default<(activity:ODClientActivityManager, client:ODClientManager_Default) => ODPromiseVoid>
//priority levels
"onPriorityLoad": ODEvent_Default<(priorities:ODPriorityManager_Default) => ODPromiseVoid>
"afterPrioritiesLoaded": ODEvent_Default<(priorities:ODPriorityManager_Default) => ODPromiseVoid>
//client slash commands
"onSlashCommandLoad": ODEvent_Default<(slash:ODSlashCommandManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
"afterSlashCommandsLoaded": ODEvent_Default<(slash:ODSlashCommandManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
"onSlashCommandRegister": ODEvent_Default<(slash:ODSlashCommandManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
"afterSlashCommandsRegistered": ODEvent_Default<(slash:ODSlashCommandManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
//client context menus
"onContextMenuLoad": ODEvent_Default<(menu:ODContextMenuManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
"afterContextMenusLoaded": ODEvent_Default<(menu:ODContextMenuManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
"onContextMenuRegister": ODEvent_Default<(menu:ODContextMenuManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
"afterContextMenusRegistered": ODEvent_Default<(menu:ODContextMenuManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
//client text commands
"onTextCommandLoad": ODEvent_Default<(text:ODTextCommandManager_Default, client:ODClientManager_Default,) => ODPromiseVoid>
"afterTextCommandsLoaded": ODEvent_Default<(text:ODTextCommandManager_Default, client:ODClientManager_Default) => ODPromiseVoid>
@@ -191,6 +202,12 @@ export interface ODEventIds_Default {
"afterTicketRenamed": ODEvent_Default<(ticket:ODTicket, renamer:discord.User, channel:discord.GuildTextBasedChannel, reason:string|null) => ODPromiseVoid>
"onTicketsClear": ODEvent_Default<(tickets:ODTicket[], clearer:discord.User, channel:discord.GuildTextBasedChannel, filter:ODTicketClearFilter) => ODPromiseVoid>
"afterTicketsCleared": ODEvent_Default<(tickets:ODTicket[], clearer:discord.User, channel:discord.GuildTextBasedChannel, filter:ODTicketClearFilter) => ODPromiseVoid>
"onTicketTopicChange": ODEvent_Default<(ticket:ODTicket, changer:discord.User, channel:discord.GuildTextBasedChannel, oldTopic:string, newTopic:string) => ODPromiseVoid>
"afterTicketTopicChanged": ODEvent_Default<(ticket:ODTicket, changer:discord.User, channel:discord.GuildTextBasedChannel, oldTopic:string, newTopic:string) => ODPromiseVoid>
"onTicketPriorityChange": ODEvent_Default<(ticket:ODTicket, changer:discord.User, channel:discord.GuildTextBasedChannel, oldPriority:ODPriorityLevel, newPriority:ODPriorityLevel, reason:string|null) => ODPromiseVoid>
"afterTicketPriorityChanged": ODEvent_Default<(ticket:ODTicket, changer:discord.User, channel:discord.GuildTextBasedChannel, oldPriority:ODPriorityLevel, newPriority:ODPriorityLevel, reason:string|null) => ODPromiseVoid>
"onTicketTransfer": ODEvent_Default<(ticket:ODTicket, changer:discord.User, channel:discord.GuildTextBasedChannel, oldCreator:discord.User, newCreator:discord.User, reason:string|null) => ODPromiseVoid>
"afterTicketTransferred": ODEvent_Default<(ticket:ODTicket, changer:discord.User, channel:discord.GuildTextBasedChannel, oldCreator:discord.User, newCreator:discord.User, reason:string|null) => ODPromiseVoid>
//roles
"onRoleLoad": ODEvent_Default<(roles:ODRoleManager) => ODPromiseVoid>
@@ -249,6 +266,10 @@ export interface ODEventIds_Default {
"afterDropdownRespondersLoaded": ODEvent_Default<(dropdowns:ODDropdownResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
"onModalResponderLoad": ODEvent_Default<(modals:ODModalResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
"afterModalRespondersLoaded": ODEvent_Default<(modals:ODModalResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
"onContextMenuResponderLoad": ODEvent_Default<(menus:ODContextMenuResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
"afterContextMenuRespondersLoaded": ODEvent_Default<(menus:ODContextMenuResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
"onAutocompleteResponderLoad": ODEvent_Default<(autocomplete:ODAutocompleteResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
"afterAutocompleteRespondersLoaded": ODEvent_Default<(autocomplete:ODAutocompleteResponderManager_Default, responders:ODResponderManager_Default, actions:ODActionManager_Default) => ODPromiseVoid>
//plugin loading before finalizations
"onPluginBeforeFinalizationLoad": ODEvent_Default<() => ODPromiseVoid>,
+2
View File
@@ -23,6 +23,8 @@ export interface ODFlagManagerIds_Default {
"opendiscord:force-slash-update":ODFlag,
"opendiscord:no-compile":ODFlag,
"opendiscord:compile-only":ODFlag,
"opendiscord:silent":ODFlag,
"opendiscord:cli":ODFlag,
}
/**## ODFlagManager_Default `default_class`
+17 -2
View File
@@ -4,6 +4,16 @@
import { ODValidId } from "../modules/base"
import { ODHelpMenuCategory, ODHelpMenuCommandComponent, ODHelpMenuComponent, ODHelpMenuManager } from "../modules/helpmenu"
/** (CONTRIBUTOR GUIDE) HOW TO ADD NEW COMMANDS?
* - Register the command in loadAllSlashCommands() & loadAllTextCommands() in (./src/data/framework/commandLoader.ts)
* - Add autocomplete for the command in OD(Slash/Text)CommandManagerIds_Default in (./src/core/api/defaults/client.ts)
* - Add the command to the help menu in (./src/data/framework/helpMenuLoader.ts)
* - If required, new config variables should be added (incl. logs, dm-logs & permissions).
* - Update the Open Ticket Documentation.
* - If the command contains complex logic or can be executed from a button/dropdown, it should be placed inside an `ODAction`.
* - Check all files, test the bot carefully & try a lot of different scenario's with different settings.
*/
/**## ODHelpMenuManagerIds_Default `interface`
* This interface is a list of ids available in the `ODHelpMenuManager_Default` class.
* It's used to generate typescript declarations for this class.
@@ -172,7 +182,8 @@ export interface ODHelpMenuManagerCategoryIds_DefaultTicketUser {
"opendiscord:claim":ODHelpMenuCommandComponent,
"opendiscord:unclaim":ODHelpMenuCommandComponent,
"opendiscord:add":ODHelpMenuCommandComponent,
"opendiscord:remove":ODHelpMenuCommandComponent
"opendiscord:remove":ODHelpMenuCommandComponent,
"opendiscord:transfer":ODHelpMenuCommandComponent,
}
/**## ODHelpMenuCategory_DefaultTicketUser `default_class`
@@ -255,7 +266,11 @@ export interface ODHelpMenuManagerCategoryIds_DefaultAdvanced {
"opendiscord:stats-ticket":ODHelpMenuCommandComponent,
"opendiscord:stats-user":ODHelpMenuCommandComponent,
"opendiscord:autoclose-disable":ODHelpMenuCommandComponent,
"opendiscord:autoclose-enable":ODHelpMenuCommandComponent
"opendiscord:autoclose-enable":ODHelpMenuCommandComponent,
"opendiscord:autodelete-disable":ODHelpMenuCommandComponent,
"opendiscord:autodelete-enable":ODHelpMenuCommandComponent,
"opendiscord:topic-set":ODHelpMenuCommandComponent,
"opendiscord:priority-set":ODHelpMenuCommandComponent,
}
/**## ODHelpMenuCategory_DefaultAdvanced `default_class`
+185 -48
View File
@@ -4,6 +4,60 @@
import { ODValidId } from "../modules/base"
import { ODLanguageManager, ODLanguage } from "../modules/language"
/** (CONTRIBUTOR GUIDE) HOW TO ADD NEW LANGUAGES?
* - Add the file to (./languages/) and make sure the metadata is valid.
* - Register the language in loadAllLanguages() in (./src/data/framework/languageLoader.ts).
* - Add autocomplete for the language in ODLanguageManagerIds_Default in (./src/core/api/defaults/language.ts).
* - Update the language list in the README.md translator list.
* - Update the 2 language counters in the README.md features list.
* - Update the Open Ticket Documentation.
*/
/**## ODLanguageManagerIds_Default `interface`
* This interface is a list of ids available in the `ODLanguageManager_Default` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODLanguageManagerIds_Default {
"opendiscord:custom":ODLanguage,
"opendiscord:english":ODLanguage,
"opendiscord:dutch":ODLanguage,
"opendiscord:portuguese":ODLanguage,
"opendiscord:czech":ODLanguage,
"opendiscord:german":ODLanguage,
"opendiscord:catalan":ODLanguage,
"opendiscord:hungarian":ODLanguage,
"opendiscord:spanish":ODLanguage,
"opendiscord:romanian":ODLanguage,
"opendiscord:ukrainian":ODLanguage,
"opendiscord:indonesian":ODLanguage,
"opendiscord:italian":ODLanguage,
"opendiscord:estonian":ODLanguage,
"opendiscord:finnish":ODLanguage,
"opendiscord:danish":ODLanguage,
"opendiscord:thai":ODLanguage,
"opendiscord:turkish":ODLanguage,
"opendiscord:french":ODLanguage,
"opendiscord:arabic":ODLanguage,
"opendiscord:hindi":ODLanguage,
"opendiscord:lithuanian":ODLanguage,
"opendiscord:polish":ODLanguage,
"opendiscord:latvian":ODLanguage,
"opendiscord:norwegian":ODLanguage,
"opendiscord:russian":ODLanguage,
"opendiscord:swedish":ODLanguage,
"opendiscord:vietnamese":ODLanguage,
"opendiscord:persian":ODLanguage,
"opendiscord:bengali":ODLanguage,
"opendiscord:greek":ODLanguage,
"opendiscord:japanese":ODLanguage,
"opendiscord:korean":ODLanguage,
"opendiscord:kurdish":ODLanguage,
"opendiscord:simplified-chinese":ODLanguage,
"opendiscord:slovenian":ODLanguage,
"opendiscord:tamil":ODLanguage,
//ADD NEW LANGUAGES HERE!!!
}
/**## ODLanguageManagerTranslations_Default `type`
* This interface is a list of ids available in the `ODLanguageManager_Default` class.
* It's used to generate typescript declarations for this class.
@@ -22,6 +76,7 @@ export type ODLanguageManagerTranslations_Default = (
"checker.system.dataPath"|
"checker.system.dataDocs"|
"checker.system.dataMessages"|
"checker.messages.invalidType"|
"checker.messages.propertyMissing"|
"checker.messages.propertyOptional"|
@@ -29,6 +84,7 @@ export type ODLanguageManagerTranslations_Default = (
"checker.messages.nullInvalid"|
"checker.messages.switchInvalidType"|
"checker.messages.objectSwitchInvalid"|
"checker.messages.stringTooShort"|
"checker.messages.stringTooLong"|
"checker.messages.stringLengthInvalid"|
@@ -37,6 +93,15 @@ export type ODLanguageManagerTranslations_Default = (
"checker.messages.stringContains"|
"checker.messages.stringChoices"|
"checker.messages.stringRegex"|
"checker.messages.stringInvertedContains"|
"checker.messages.stringLowercase"|
"checker.messages.stringUppercase"|
"checker.messages.stringSpecialCharacters"|
"checker.messages.stringNoSpaces"|
"checker.messages.stringCapitalWord"|
"checker.messages.stringCapitalSentence"|
"checker.messages.stringPunctuation"|
"checker.messages.numberTooShort"|
"checker.messages.numberTooLong"|
"checker.messages.numberLengthInvalid"|
@@ -53,8 +118,12 @@ export type ODLanguageManagerTranslations_Default = (
"checker.messages.numberNegative"|
"checker.messages.numberPositive"|
"checker.messages.numberZero"|
"checker.messages.numberNan"|
"checker.messages.numberInvertedContains"|
"checker.messages.booleanTrue"|
"checker.messages.booleanFalse"|
"checker.messages.arrayEmptyDisabled"|
"checker.messages.arrayEmptyRequired"|
"checker.messages.arrayTooShort"|
@@ -62,6 +131,7 @@ export type ODLanguageManagerTranslations_Default = (
"checker.messages.arrayLengthInvalid"|
"checker.messages.arrayInvalidTypes"|
"checker.messages.arrayDouble"|
"checker.messages.discordInvalidId"|
"checker.messages.discordInvalidIdOptions"|
"checker.messages.discordInvalidToken"|
@@ -83,6 +153,8 @@ export type ODLanguageManagerTranslations_Default = (
"checker.messages.unusedOption"|
"checker.messages.unusedQuestion"|
"checker.messages.dropdownOption"|
"checker.messages.customInvalidVersion"|
"actions.buttons.create"|
"actions.buttons.close"|
"actions.buttons.delete"|
@@ -120,6 +192,7 @@ export type ODLanguageManagerTranslations_Default = (
"actions.titles.blacklistAddDm"|
"actions.titles.blacklistRemoveDm"|
"actions.titles.clear"|
"actions.titles.clearTickets"|
"actions.titles.roles"|
"actions.titles.autoclose"|
@@ -129,6 +202,11 @@ export type ODLanguageManagerTranslations_Default = (
"actions.titles.autodeleteEnabled"|
"actions.titles.autodeleteDisabled"|
"actions.titles.topicSet"|
"actions.titles.prioritySet"|
"actions.titles.priorityGet"|
"actions.titles.transfer"|
"actions.descriptions.create"|
"actions.descriptions.close"|
"actions.descriptions.delete"|
@@ -169,6 +247,11 @@ export type ODLanguageManagerTranslations_Default = (
"actions.descriptions.ticketMessageAutodelete"|
"actions.descriptions.panelReady"|
"actions.descriptions.topicSet"|
"actions.descriptions.prioritySet"|
"actions.descriptions.priorityGet"|
"actions.descriptions.transfer"|
"actions.modal.closePlaceholder"|
"actions.modal.deletePlaceholder"|
"actions.modal.reopenPlaceholder"|
@@ -207,6 +290,13 @@ export type ODLanguageManagerTranslations_Default = (
"actions.logs.blacklistRemoveDm"|
"actions.logs.clearLog"|
"actions.logs.transferLog"|
"actions.logs.transferDm"|
"actions.logs.prioritySetLog"|
"actions.logs.prioritySetDm"|
"actions.logs.roleUpdateLog"|
"actions.logs.roleUpdateDm"|
"transcripts.success.visit"|
"transcripts.success.ready"|
"transcripts.success.textFileDescription"|
@@ -223,6 +313,17 @@ export type ODLanguageManagerTranslations_Default = (
"transcripts.errors.continue"|
"transcripts.errors.backup"|
"transcripts.errors.error"|
"transcripts.errors.title"|
"transcripts.text.messagesTitle"|
"transcripts.text.embedTitle"|
"transcripts.text.fileTitle"|
"transcripts.text.fieldsTitle"|
"transcripts.text.reactionsTitle"|
"transcripts.text.statsTitle"|
"transcripts.text.emptyContent"|
"transcripts.text.noTitle"|
"transcripts.text.noDesc"|
"errors.titles.internalError"|
"errors.titles.optionMissing"|
@@ -236,6 +337,7 @@ export type ODLanguageManagerTranslations_Default = (
"errors.titles.notInGuild"|
"errors.titles.channelRename"|
"errors.titles.busy"|
"errors.titles.permissionError"|
"errors.descriptions.askForInfo"|
"errors.descriptions.askForInfoResolve"|
@@ -258,6 +360,9 @@ export type ODLanguageManagerTranslations_Default = (
"errors.descriptions.channelRename"|
"errors.descriptions.channelRenameSource"|
"errors.descriptions.busy"|
"errors.descriptions.closeBeforeMessage"|
"errors.descriptions.closeBeforeAdminMessage"|
"errors.descriptions.unableToCreateTicket"|
"errors.optionInvalidReasons.stringRegex"|
"errors.optionInvalidReasons.stringMinLength"|
@@ -302,7 +407,6 @@ export type ODLanguageManagerTranslations_Default = (
"params.uppercase.added"|
"params.uppercase.removed"|
"params.uppercase.filter"|
"params.uppercase.claimedBy"|
"params.uppercase.method"|
"params.uppercase.type"|
"params.uppercase.blacklisted"|
@@ -328,6 +432,27 @@ export type ODLanguageManagerTranslations_Default = (
"params.uppercase.pinned"|
"params.uppercase.creationDate"|
"params.uppercase.noone"|
"params.uppercase.open"|
"params.uppercase.closed"|
"params.uppercase.priority"|
"params.uppercase.status"|
"params.uppercase.participants"|
"params.uppercase.yes"|
"params.uppercase.no"|
"params.uppercase.option"|
"params.uppercase.topic"|
"params.uppercase.uptime"|
"params.uppercase.messages"|
"params.uppercase.embeds"|
"params.uppercase.files"|
"params.uppercase.components"|
"params.uppercase.cooldown"|
"params.uppercase.maxTickets"|
"params.uppercase.admins"|
"params.uppercase.roles"|
"params.uppercase.size"|
"params.lowercase.text"|
"params.lowercase.html"|
"params.lowercase.command"|
@@ -394,6 +519,18 @@ export type ODLanguageManagerTranslations_Default = (
"commands.autodeleteEnable"|
"commands.autodeleteEnableTime"|
"commands.topic"|
"commands.topicSet"|
"commands.topicValue"|
"commands.topicList"|
"commands.priority"|
"commands.prioritySet"|
"commands.priorityValue"|
"commands.priorityGet"|
"commands.priorityList"|
"commands.transfer"|
"commands.transferUser"|
"helpMenu.help"|
"helpMenu.ticket"|
"helpMenu.close"|
@@ -421,11 +558,20 @@ export type ODLanguageManagerTranslations_Default = (
"helpMenu.autodeleteDisable"|
"helpMenu.autodeleteEnable"|
"helpMenu.categories.general"|
"helpMenu.categories.basicTicket"|
"helpMenu.categories.advancedTicket"|
"helpMenu.categories.userTicket"|
"helpMenu.categories.admin"|
"helpMenu.categories.advanced"|
"helpMenu.categories.extra"|
"stats.scopes.global"|
"stats.scopes.system"|
"stats.scopes.user"|
"stats.scopes.ticket"|
"stats.scopes.participants"|
"stats.scopes.messages"|
"stats.properties.ticketsCreated"|
"stats.properties.ticketsClosed"|
@@ -436,55 +582,46 @@ export type ODLanguageManagerTranslations_Default = (
"stats.properties.ticketsPinned"|
"stats.properties.ticketsMoved"|
"stats.properties.usersBlacklisted"|
"stats.properties.transcriptsCreated"
"stats.properties.transcriptsCreated"|
"stats.properties.ticketsAutodeleted"|
"stats.properties.ticketsTransferred"|
"stats.properties.ticketVolume"|
"stats.properties.averageTickets"|
"stats.properties.currentTickets"|
"stats.properties.age"|
"stats.properties.responseTime"|
"stats.properties.resolutionTime"|
"stats.properties.createdOn"|
"stats.properties.createdBy"|
"stats.properties.closedOn"|
"stats.properties.closedBy"|
"stats.properties.claimedOn"|
"stats.properties.claimedBy"|
"stats.properties.pinnedOn"|
"stats.properties.pinnedBy"|
"stats.properties.deletedOn"|
"stats.properties.deletedBy"|
"stats.roles.developer"|
"stats.roles.serverOwner"|
"stats.roles.serverAdmin"|
"stats.roles.moderator"|
"stats.roles.support"|
"stats.roles.member"|
"panel.selectTicket"|
"panel.selectRole"|
"panel.selectOption"|
"priorities.urgent"|
"priorities.veryHigh"|
"priorities.high"|
"priorities.normal"|
"priorities.low"|
"priorities.veryLow"|
"priorities.none"
)
/**## ODLanguageManagerIds_Default `interface`
* This interface is a list of ids available in the `ODLanguageManager_Default` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODLanguageManagerIds_Default {
"opendiscord:custom":ODLanguage,
"opendiscord:english":ODLanguage,
"opendiscord:dutch":ODLanguage,
"opendiscord:portuguese":ODLanguage,
"opendiscord:czech":ODLanguage,
"opendiscord:german":ODLanguage,
"opendiscord:catalan":ODLanguage,
"opendiscord:hungarian":ODLanguage,
"opendiscord:spanish":ODLanguage,
"opendiscord:romanian":ODLanguage,
"opendiscord:ukrainian":ODLanguage,
"opendiscord:indonesian":ODLanguage,
"opendiscord:italian":ODLanguage,
"opendiscord:estonian":ODLanguage,
"opendiscord:finnish":ODLanguage,
"opendiscord:danish":ODLanguage,
"opendiscord:thai":ODLanguage,
"opendiscord:turkish":ODLanguage,
"opendiscord:french":ODLanguage,
"opendiscord:arabic":ODLanguage,
"opendiscord:hindi":ODLanguage,
"opendiscord:lithuanian":ODLanguage,
"opendiscord:polish":ODLanguage,
"opendiscord:latvian":ODLanguage,
"opendiscord:norwegian":ODLanguage,
"opendiscord:russian":ODLanguage,
"opendiscord:swedish":ODLanguage,
"opendiscord:vietnamese":ODLanguage,
"opendiscord:persian":ODLanguage,
"opendiscord:bengali":ODLanguage,
"opendiscord:greek":ODLanguage,
"opendiscord:japanese":ODLanguage,
"opendiscord:korean":ODLanguage,
"opendiscord:kurdish":ODLanguage,
"opendiscord:simplified-chinese":ODLanguage,
"opendiscord:slovenian":ODLanguage,
"opendiscord:tamil":ODLanguage,
//ADD NEW LANGUAGES HERE!!!
}
/**## ODLanguageManager_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODLanguageManager class.
* It doesn't add any extra features!
+3
View File
@@ -134,6 +134,9 @@ export interface ODProgressBarManagerIds_Default {
"opendiscord:slash-command-remove":ODManualProgressBar,
"opendiscord:slash-command-create":ODManualProgressBar,
"opendiscord:slash-command-update":ODManualProgressBar,
"opendiscord:context-menu-remove":ODManualProgressBar,
"opendiscord:context-menu-create":ODManualProgressBar,
"opendiscord:context-menu-update":ODManualProgressBar,
}
/**## ODProgressBarManager_Default `default_class`
+101 -1
View File
@@ -2,7 +2,7 @@
//DEFAULT RESPONDER MODULE
///////////////////////////////////////
import { ODValidId } from "../modules/base"
import { ODButtonResponder, ODButtonResponderInstance, ODButtonResponderManager, ODCommandResponder, ODCommandResponderInstance, ODCommandResponderManager, ODDropdownResponder, ODDropdownResponderInstance, ODDropdownResponderManager, ODModalResponder, ODModalResponderInstance, ODModalResponderManager, ODResponderManager } from "../modules/responder"
import { ODAutocompleteResponder, ODAutocompleteResponderInstance, ODAutocompleteResponderManager, ODButtonResponder, ODButtonResponderInstance, ODButtonResponderManager, ODCommandResponder, ODCommandResponderInstance, ODCommandResponderManager, ODContextMenuResponder, ODContextMenuResponderInstance, ODContextMenuResponderManager, ODDropdownResponder, ODDropdownResponderInstance, ODDropdownResponderManager, ODModalResponder, ODModalResponderInstance, ODModalResponderManager, ODResponderManager } from "../modules/responder"
import { ODWorkerManager_Default } from "./worker"
/**## ODResponderManager_Default `default_class`
@@ -16,6 +16,8 @@ export class ODResponderManager_Default extends ODResponderManager {
declare buttons: ODButtonResponderManager_Default
declare dropdowns: ODDropdownResponderManager_Default
declare modals: ODModalResponderManager_Default
declare contextMenus: ODContextMenuResponderManager_Default
declare autocomplete: ODAutocompleteResponderManager_Default
}
/**## ODCommandResponderManagerIds_Default `interface`
@@ -42,6 +44,9 @@ export interface ODCommandResponderManagerIds_Default {
"opendiscord:add":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:add"|"opendiscord:logs"},
"opendiscord:remove":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:remove"|"opendiscord:logs"},
"opendiscord:clear":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:clear"|"opendiscord:logs"},
"opendiscord:topic":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:topic"|"opendiscord:logs"},
"opendiscord:priority":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:priority"|"opendiscord:logs"},
"opendiscord:transfer":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:transfer"|"opendiscord:logs"},
"opendiscord:autoclose":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:autoclose"|"opendiscord:logs"},
"opendiscord:autodelete":{source:"slash"|"text",params:{},workers:"opendiscord:permissions"|"opendiscord:autodelete"|"opendiscord:logs"},
@@ -253,3 +258,98 @@ export class ODModalResponderManager_Default extends ODModalResponderManager {
export class ODModalResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODModalResponder<Source,Params> {
declare workers: ODWorkerManager_Default<ODModalResponderInstance,Source,Params,WorkerIds>
}
/**## ODContextMenuResponderManagerIds_Default `interface`
* This interface is a list of ids available in the `ODContextMenuResponderManager_Default` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODContextMenuResponderManagerIds_Default {
//"opendiscord:example":{source:"context-menu",params:{},workers:"opendiscord:example"},
}
/**## ODContextMenuResponderManager_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODContextMenuResponderManager class.
* It doesn't add any extra features!
*
* This default class is made for the global variable `opendiscord.responders.contextMenus`!
*/
export class ODContextMenuResponderManager_Default extends ODContextMenuResponderManager {
get<ModalResponderId extends keyof ODContextMenuResponderManagerIds_Default>(id:ModalResponderId): ODContextMenuResponder_Default<ODContextMenuResponderManagerIds_Default[ModalResponderId]["source"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["params"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["workers"]>
get(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null
get(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null {
return super.get(id)
}
remove<ModalResponderId extends keyof ODContextMenuResponderManagerIds_Default>(id:ModalResponderId): ODContextMenuResponder_Default<ODContextMenuResponderManagerIds_Default[ModalResponderId]["source"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["params"],ODContextMenuResponderManagerIds_Default[ModalResponderId]["workers"]>
remove(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null
remove(id:ODValidId): ODContextMenuResponder<"context-menu",any>|null {
return super.remove(id)
}
exists(id:keyof ODContextMenuResponderManagerIds_Default): boolean
exists(id:ODValidId): boolean
exists(id:ODValidId): boolean {
return super.exists(id)
}
}
/**## ODContextMenuResponder_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODContextMenuResponder class.
* It doesn't add any extra features!
*
* This default class is made for the default `ODContextMenuResponder`'s!
*/
export class ODContextMenuResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODContextMenuResponder<Source,Params> {
declare workers: ODWorkerManager_Default<ODContextMenuResponderInstance,Source,Params,WorkerIds>
}
/**## ODAutocompleteResponderManagerIds_Default `interface`
* This interface is a list of ids available in the `ODAutocompleteResponderManager_Default` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODAutocompleteResponderManagerIds_Default {
"opendiscord:panel-id":{source:"autocomplete",params:{},workers:"opendiscord:panel-id"},
"opendiscord:option-id":{source:"autocomplete",params:{},workers:"opendiscord:option-id"}
}
/**## ODAutocompleteResponderManager_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODAutocompleteResponderManager class.
* It doesn't add any extra features!
*
* This default class is made for the global variable `opendiscord.responders.autocomplete`!
*/
export class ODAutocompleteResponderManager_Default extends ODAutocompleteResponderManager {
get<ModalResponderId extends keyof ODAutocompleteResponderManagerIds_Default>(id:ModalResponderId): ODAutocompleteResponder_Default<ODAutocompleteResponderManagerIds_Default[ModalResponderId]["source"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["params"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["workers"]>
get(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null
get(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null {
return super.get(id)
}
remove<ModalResponderId extends keyof ODAutocompleteResponderManagerIds_Default>(id:ModalResponderId): ODAutocompleteResponder_Default<ODAutocompleteResponderManagerIds_Default[ModalResponderId]["source"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["params"],ODAutocompleteResponderManagerIds_Default[ModalResponderId]["workers"]>
remove(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null
remove(id:ODValidId): ODAutocompleteResponder<"autocomplete",any>|null {
return super.remove(id)
}
exists(id:keyof ODAutocompleteResponderManagerIds_Default): boolean
exists(id:ODValidId): boolean
exists(id:ODValidId): boolean {
return super.exists(id)
}
}
/**## ODAutocompleteResponder_Default `default_class`
* This is a special class that adds type definitions & typescript to the ODAutocompleteResponder class.
* It doesn't add any extra features!
*
* This default class is made for the default `ODAutocompleteResponder`'s!
*/
export class ODAutocompleteResponder_Default<Source extends string, Params, WorkerIds extends string> extends ODAutocompleteResponder<Source,Params> {
declare workers: ODWorkerManager_Default<ODAutocompleteResponderInstance,Source,Params,WorkerIds>
}
+114 -5
View File
@@ -1,7 +1,6 @@
///////////////////////////////////////
//DEFAULT SESSION MODULE
///////////////////////////////////////
import { Guild, TextBasedChannel, User } from "discord.js"
import { ODValidId } from "../modules/base"
import { ODStatScope, ODStatGlobalScope, ODStatsManager, ODStat, ODBasicStat, ODDynamicStat, ODValidStatValue, ODStatScopeSetMode } from "../modules/stat"
@@ -14,7 +13,8 @@ export interface ODStatsManagerIds_Default {
"opendiscord:system":ODStatGlobalScope_DefaultSystem,
"opendiscord:user":ODStatScope_DefaultUser,
"opendiscord:ticket":ODStatScope_DefaultTicket,
"opendiscord:participants":ODStatScope_DefaultParticipants
"opendiscord:participants":ODStatScope_DefaultParticipants,
"opendiscord:messages":ODStatScope_DefaultMessages,
}
/**## ODStatsManager_Default `default_class`
@@ -60,8 +60,11 @@ export interface ODStatGlobalScopeIds_DefaultGlobal {
"opendiscord:tickets-claimed":ODBasicStat,
"opendiscord:tickets-pinned":ODBasicStat,
"opendiscord:tickets-moved":ODBasicStat,
"opendiscord:tickets-transferred":ODBasicStat,
"opendiscord:users-blacklisted":ODBasicStat,
"opendiscord:transcripts-created":ODBasicStat
"opendiscord:transcripts-created":ODBasicStat,
"opendiscord:ticket-volume":ODDynamicStat,
"opendiscord:average-tickets":ODDynamicStat,
}
/**## ODStatGlobalScope_DefaultGlobal `default_class`
@@ -99,6 +102,13 @@ export class ODStatGlobalScope_DefaultGlobal extends ODStatGlobalScope {
return super.getStat(id)
}
getAllStats<StatsId extends keyof ODStatGlobalScopeIds_DefaultGlobal>(id:StatsId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
return super.getAllStats(id)
}
setStat<StatsId extends keyof ODStatGlobalScopeIds_DefaultGlobal>(id:StatsId, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
@@ -120,6 +130,7 @@ export class ODStatGlobalScope_DefaultGlobal extends ODStatGlobalScope {
*/
export interface ODStatGlobalScopeIds_DefaultSystem {
"opendiscord:startup-date":ODDynamicStat,
"opendiscord:system-uptime":ODDynamicStat,
"opendiscord:version":ODDynamicStat
}
@@ -158,6 +169,13 @@ export class ODStatGlobalScope_DefaultSystem extends ODStatGlobalScope {
return super.getStat(id)
}
getAllStats<StatsId extends keyof ODStatGlobalScopeIds_DefaultSystem>(id:StatsId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
return super.getAllStats(id)
}
setStat<StatsId extends keyof ODStatGlobalScopeIds_DefaultSystem>(id:StatsId, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
@@ -187,8 +205,10 @@ export interface ODStatScopeIds_DefaultUser {
"opendiscord:tickets-claimed":ODBasicStat,
"opendiscord:tickets-pinned":ODBasicStat,
"opendiscord:tickets-moved":ODBasicStat,
"opendiscord:tickets-transferred":ODBasicStat,
"opendiscord:users-blacklisted":ODBasicStat,
"opendiscord:transcripts-created":ODBasicStat
"opendiscord:transcripts-created":ODBasicStat,
"opendiscord:current-tickets":ODDynamicStat,
}
/**## ODStatScope_DefaultUser `default_class`
@@ -226,6 +246,13 @@ export class ODStatScope_DefaultUser extends ODStatScope {
return super.getStat(id,scopeId)
}
getAllStats<StatsId extends keyof ODStatScopeIds_DefaultUser>(id:StatsId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
return super.getAllStats(id)
}
setStat<StatsId extends keyof ODStatScopeIds_DefaultUser>(id:StatsId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
@@ -251,7 +278,10 @@ export interface ODStatScopeIds_DefaultTicket {
"opendiscord:claimed":ODDynamicStat,
"opendiscord:pinned":ODDynamicStat,
"opendiscord:creation-date":ODDynamicStat,
"opendiscord:creator":ODDynamicStat
"opendiscord:creator":ODDynamicStat,
"opendiscord:ticket-age":ODDynamicStat,
"opendiscord:response-time":ODDynamicStat,
"opendiscord:resolution-time":ODDynamicStat,
}
/**## ODStatScope_DefaultTicket `default_class`
@@ -289,6 +319,13 @@ export class ODStatScope_DefaultTicket extends ODStatScope {
return super.getStat(id,scopeId)
}
getAllStats<StatsId extends keyof ODStatScopeIds_DefaultTicket>(id:StatsId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
return super.getAllStats(id)
}
setStat<StatsId extends keyof ODStatScopeIds_DefaultTicket>(id:StatsId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
@@ -347,6 +384,13 @@ export class ODStatScope_DefaultParticipants extends ODStatScope {
return super.getStat(id,scopeId)
}
getAllStats<StatsId extends keyof ODStatScopeIds_DefaultParticipants>(id:StatsId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
return super.getAllStats(id)
}
setStat<StatsId extends keyof ODStatScopeIds_DefaultParticipants>(id:StatsId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
@@ -361,3 +405,68 @@ export class ODStatScope_DefaultParticipants extends ODStatScope {
return super.resetStat(id,scopeId)
}
}
/**## ODStatScopeIds_DefaultMessages `type`
* This interface is a list of ids available in the `ODStatScope_DefaultMessages` class.
* It's used to generate typescript declarations for this class.
*/
export interface ODStatScopeIds_DefaultMessages {
"opendiscord:count":ODDynamicStat
}
/**## ODStatScope_DefaultMessages `default_class`
* This is a special class that adds type definitions & typescript to the ODStatsManager class.
* It doesn't add any extra features!
*
* This default class is made for the `opendiscord:participants` category in `opendiscord.stats`!
*/
export class ODStatScope_DefaultMessages extends ODStatScope {
get<StatsId extends keyof ODStatScopeIds_DefaultMessages>(id:StatsId): ODStatScopeIds_DefaultMessages[StatsId]
get(id:ODValidId): ODStat|null
get(id:ODValidId): ODStat|null {
return super.get(id)
}
remove<StatsId extends keyof ODStatScopeIds_DefaultMessages>(id:StatsId): ODStatScopeIds_DefaultMessages[StatsId]
remove(id:ODValidId): ODStat|null
remove(id:ODValidId): ODStat|null {
return super.remove(id)
}
exists(id:keyof ODStatScopeIds_DefaultMessages): boolean
exists(id:ODValidId): boolean
exists(id:ODValidId): boolean {
return super.exists(id)
}
getStat<StatsId extends keyof ODStatScopeIds_DefaultMessages>(id:StatsId, scopeId:string): Promise<ODValidStatValue|null>
getStat(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null>
getStat(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null> {
return super.getStat(id,scopeId)
}
getAllStats<StatsId extends keyof ODStatScopeIds_DefaultMessages>(id:StatsId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]>
getAllStats(id:ODValidId): Promise<{id:string,value:ODValidStatValue}[]> {
return super.getAllStats(id)
}
setStat<StatsId extends keyof ODStatScopeIds_DefaultMessages>(id:StatsId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean>
setStat(id:ODValidId, scopeId:string, value:ODValidStatValue, mode:ODStatScopeSetMode): Promise<boolean> {
return super.setStat(id,scopeId,value,mode)
}
resetStat<StatsId extends keyof ODStatScopeIds_DefaultMessages>(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null>
resetStat(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null>
resetStat(id:ODValidId, scopeId:string): Promise<ODValidStatValue|null> {
return super.resetStat(id,scopeId)
}
}
+7 -3
View File
@@ -37,6 +37,7 @@ import { ODQuestionManager } from "./openticket/question"
import { ODBlacklistManager } from "./openticket/blacklist"
import { ODTranscriptManager_Default } from "./openticket/transcript"
import { ODRoleManager } from "./openticket/role"
import { ODPriorityManager_Default } from "./openticket/priority"
/**## ODMain `class`
* This is the main Open Ticket class.
@@ -127,12 +128,14 @@ export class ODMain {
transcripts: ODTranscriptManager_Default
/**The manager that manages all reaction roles in the bot. (here, you can add additional data to roles) */
roles: ODRoleManager
/**The manager that manages all priority levels in the bot. (register/edit ticket priority levels) */
priorities: ODPriorityManager_Default
constructor(){
this.versions = new ODVersionManager_Default()
this.versions.add(ODVersion.fromString("opendiscord:version","v4.0.7"))
this.versions.add(ODVersion.fromString("opendiscord:version","v4.1.0"))
this.versions.add(ODVersion.fromString("opendiscord:api","v1.0.0"))
this.versions.add(ODVersion.fromString("opendiscord:transcripts","v2.0.0"))
this.versions.add(ODVersion.fromString("opendiscord:transcripts","v2.1.0"))
this.versions.add(ODVersion.fromString("opendiscord:livestatus","v2.0.0"))
this.debugfile = new ODDebugFileManager("./","otdebug.txt",5000,this.versions.get("opendiscord:version"))
@@ -173,8 +176,9 @@ export class ODMain {
this.panels = new ODPanelManager(this.debug)
this.tickets = new ODTicketManager(this.debug,this.client)
this.blacklist = new ODBlacklistManager(this.debug)
this.transcripts = new ODTranscriptManager_Default(this.debug,this.tickets,this.client)
this.transcripts = new ODTranscriptManager_Default(this.debug,this.tickets,this.client,this.permissions)
this.roles = new ODRoleManager(this.debug)
this.priorities = new ODPriorityManager_Default(this.debug)
}
/**Log a message to the console. But in the Open Ticket style :) */
+6 -94
View File
@@ -63,16 +63,6 @@ export class ODId {
get value(){
return this.#value
}
/**The source of the id (text before `:`). (e.g. `openticket` for all built-in ids)
*
* @deprecated Replaced with `getNamespace()` and will be removed in `v4.1.0`.
*/
source: string
/**The identifier of the id (text after `:`).
*
* @deprecated Replaced with `getIdentifier()` and will be removed in `v4.1.0`.
*/
identifier: string
/**The change listener for the parent `ODManager` of this `ODId`. */
#change: ((oldId:string,newId:string) => void)|null = null
@@ -92,21 +82,9 @@ export class ODId {
if (result.length > 0) this.#value = result.join("")
else throw new ODSystemError("invalid ID at 'new ODID(id: "+id+")'")
const splitted = this.#value.split(":")
if (splitted.length > 1){
this.source = splitted[0]
splitted.shift()
this.identifier = splitted.join(":")
}else{
this.identifier = splitted.join(":")
this.source = ""
}
}else{
//id is ODId
this.#value = id.#value
this.source = id.source
this.identifier = id.identifier
}
}
@@ -171,47 +149,6 @@ export class ODManagerChangeHelper {
}
}
/**## ODManagerRedirectHelper `class`
* @deprecated ### Will be removed in Open Ticket `v4.1.0`!
*
* This is Open Ticket ticket manager redirect helper.
*
* It is used to redirect a source to another source when the id isn't found.
*
* It will be used in **Open Discord** to allow plugins from all projects to work seamlessly!
* ## **(❌ SYSTEM ONLY!!)**
*/
export class ODManagerRedirectHelper {
#data: {fromSource:string,toSource:string}[] = []
/****(❌ SYSTEM ONLY!!)** Add a redirect to this manager. Returns `true` when overwritten. */
add(fromSource:string, toSource:string){
const index = this.#data.findIndex((data) => data.fromSource === fromSource)
if (index > -1){
//already exists
this.#data[index] = {fromSource,toSource}
return true
}else{
//doesn't exist
this.#data.push({fromSource,toSource})
return false
}
}
/****(❌ SYSTEM ONLY!!)** Remove a redirect from this manager. Returns `true` when it existed. */
remove(fromSource:string, toSource:string){
const index = this.#data.findIndex((data) => data.fromSource === fromSource && data.toSource == toSource)
if (index > -1){
//already exists
this.#data.splice(index,1)
return true
}else return false
}
/**List all redirects from this manager. */
list(){
return [...this.#data]
}
}
/**## ODManagerData `class`
* This is Open Ticket manager data.
*
@@ -261,8 +198,6 @@ export class ODManager<DataType extends ODManagerData> extends ODManagerChangeHe
#changeListeners: ODManagerCallback<DataType>[] = []
/**An array storing all listeners when data is removed. */
#removeListeners: ODManagerCallback<DataType>[] = []
/**Handle all redirects in this `ODManager` */
redirects: ODManagerRedirectHelper = new ODManagerRedirectHelper()
constructor(debug?:ODDebugger, debugname?:string){
super()
@@ -333,15 +268,7 @@ export class ODManager<DataType extends ODManagerData> extends ODManagerChangeHe
const newId = new ODId(id)
const data = this.#data.get(newId.value)
if (data) return data
else{
//DEPRECATED!!!
const redirect = this.redirects.list().find((redirect) => redirect.fromSource === newId.getNamespace())
if (!redirect) return null
else{
const redirectId = new ODId(redirect.toSource+":"+newId.getIdentifier())
return this.get(redirectId)
}
}
else return null
}
/**Remove data that matches the `ODId`. Returns the removed data. */
remove(id:ODValidId): DataType|null {
@@ -349,15 +276,8 @@ export class ODManager<DataType extends ODManagerData> extends ODManagerChangeHe
const data = this.#data.get(newId.value)
if (!data){
//DEPRECATED!!!
const redirect = this.redirects.list().find((redirect) => redirect.fromSource === newId.getNamespace())
if (!redirect){
if (this.#debug) this.#debug.debug("Removed "+this.#debugname+" from manager",[{key:"id",value:newId.value},{key:"found",value:"false"}])
return null
}else{
const redirectId = new ODId(redirect.toSource+":"+newId.getIdentifier())
return this.remove(redirectId)
}
}else{
this.#data.delete(newId.value)
if (this.#debug) this.#debug.debug("Removed "+this.#debugname+" from manager",[{key:"id",value:newId.value},{key:"found",value:"true"}])
@@ -385,15 +305,7 @@ export class ODManager<DataType extends ODManagerData> extends ODManagerChangeHe
exists(id:ODValidId): boolean {
const newId = new ODId(id)
if (this.#data.has(newId.value)) return true
else{
//DEPRECATED!!!
const redirect = this.redirects.list().find((redirect) => redirect.fromSource === newId.getNamespace())
if (!redirect) return false
else{
const redirectId = new ODId(redirect.toSource+":"+newId.getIdentifier())
return this.exists(redirectId)
}
}
else return false
}
/**Get all data inside this manager*/
getAll(): DataType[] {
@@ -648,8 +560,8 @@ export class ODHTTPGetRequest {
this.throwOnError = throwOnError
const newConfig = config ?? {}
newConfig.method = "GET"
if (newConfig.headers) Object.assign(newConfig.headers,{"User-Agent":"OpenDiscordBots-OpenTicket/4.0.7"})
else newConfig.headers = {"User-Agent":"OpenDiscordBots-OpenTicket/4.0.7"}
if (newConfig.headers) Object.assign(newConfig.headers,{"User-Agent":"OpenDiscordBots-OpenTicket/4.1.0"})
else newConfig.headers = {"User-Agent":"OpenDiscordBots-OpenTicket/4.1.0"}
this.config = newConfig
}
@@ -703,8 +615,8 @@ export class ODHTTPPostRequest {
this.throwOnError = throwOnError
const newConfig = config ?? {}
newConfig.method = "POST"
if (newConfig.headers) Object.assign(newConfig.headers,{"User-Agent":"OpenDiscordBots-OpenTicket/4.0.7"})
else newConfig.headers = {"User-Agent":"OpenDiscordBots-OpenTicket/4.0.7"}
if (newConfig.headers) Object.assign(newConfig.headers,{"User-Agent":"OpenDiscordBots-OpenTicket/4.1.0"})
else newConfig.headers = {"User-Agent":"OpenDiscordBots-OpenTicket/4.1.0"}
this.config = newConfig
}
+85 -53
View File
@@ -257,15 +257,7 @@ export class ODQuickButton {
/**The id of this button. */
id: ODId
/**The current data of this button */
data: Partial<ODButtonData> = {
customId:"",
mode:"button",
url:null,
color:null,
label:null,
emoji:null,
disabled:false
}
data: Partial<ODButtonData>
constructor(id:ODValidId,data:Partial<ODButtonData>){
this.id = new ODId(id)
@@ -277,12 +269,13 @@ export class ODQuickButton {
try {
//create the discord.js button
const button = new discord.ButtonBuilder()
if (this.data.mode == "button") button.setCustomId(this.data.customId ?? "od:unknown-button")
if (this.data.mode == "button" || (!this.data.mode && this.data.customId)) button.setCustomId(this.data.customId ?? "od:unknown-button")
if (this.data.mode == "url") button.setStyle(discord.ButtonStyle.Link)
else if (this.data.color == "gray") button.setStyle(discord.ButtonStyle.Secondary)
else if (this.data.color == "blue") button.setStyle(discord.ButtonStyle.Primary)
else if (this.data.color == "green") button.setStyle(discord.ButtonStyle.Success)
else if (this.data.color == "red") button.setStyle(discord.ButtonStyle.Danger)
else button.setStyle(discord.ButtonStyle.Secondary)
if (this.data.url) button.setURL(this.data.url)
if (this.data.label) button.setLabel(this.data.label)
if (this.data.emoji) button.setEmoji(this.data.emoji)
@@ -564,21 +557,7 @@ export class ODQuickDropdown {
/**The id of this dropdown. */
id: ODId
/**The current data of this dropdown */
data: Partial<ODDropdownData> = {
customId:"",
type:"string",
placeholder:null,
minValues:null,
maxValues:null,
disabled:false,
channelTypes:[],
options:[],
users:[],
roles:[],
channels:[],
mentionables:[]
}
data: Partial<ODDropdownData>
constructor(id:ODValidId,data:Partial<ODDropdownData>){
this.id = new ODId(id)
@@ -804,12 +783,7 @@ export class ODQuickFile {
/**The id of this file. */
id: ODId
/**The current data of this file */
data: Partial<ODFileData> = {
file:"",
name:"file.txt",
description:null,
spoiler:false
}
data: Partial<ODFileData>
constructor(id:ODValidId,data:Partial<ODFileData>){
this.id = new ODId(id)
@@ -862,7 +836,7 @@ export interface ODEmbedData {
/**The title of the embed */
title:string|null,
/**The color of the embed */
color:discord.ColorResolvable|null,
color:discord.ColorResolvable|string|null,
/**The url of the embed */
url:string|null,
/**The description of the embed */
@@ -1020,7 +994,7 @@ export class ODEmbed<Source extends string,Params> extends ODBuilderImplementati
//create the discord.js embed
const embed = new discord.EmbedBuilder()
if (instance.data.title) embed.setTitle(instance.data.title)
if (instance.data.color) embed.setColor(instance.data.color)
if (instance.data.color) embed.setColor(instance.data.color as discord.ColorResolvable)
if (instance.data.url) embed.setURL(instance.data.url)
if (instance.data.description) embed.setDescription(instance.data.description)
if (instance.data.authorText) embed.setAuthor({
@@ -1060,21 +1034,7 @@ export class ODQuickEmbed {
/**The id of this embed. */
id: ODId
/**The current data of this embed */
data: Partial<ODEmbedData> = {
title:null,
color:null,
url:null,
description:null,
authorText:null,
authorImage:null,
authorUrl:null,
footerText:null,
footerImage:null,
image:null,
thumbnail:null,
fields:[],
timestamp:null
}
data: Partial<ODEmbedData>
constructor(id:ODValidId,data:Partial<ODEmbedData>){
this.id = new ODId(id)
@@ -1087,7 +1047,7 @@ export class ODQuickEmbed {
//create the discord.js embed
const embed = new discord.EmbedBuilder()
if (this.data.title) embed.setTitle(this.data.title)
if (this.data.color) embed.setColor(this.data.color)
if (this.data.color) embed.setColor(this.data.color as discord.ColorResolvable)
if (this.data.url) embed.setURL(this.data.url)
if (this.data.description) embed.setDescription(this.data.description)
if (this.data.authorText) embed.setAuthor({
@@ -1186,13 +1146,10 @@ export class ODMessageInstance {
content:null,
poll:null,
ephemeral:false,
embeds:[],
components:[],
files:[],
additionalOptions:{
}
additionalOptions:{}
}
/**Set the content of this message */
@@ -1354,6 +1311,81 @@ export class ODMessage<Source extends string,Params> extends ODBuilderImplementa
}
}
/**## ODQuickMessage `class`
* This is an Open Ticket quick message builder.
*
* With this class, you can quickly create a message to send in a discord channel.
* This quick message can be used by Open Ticket plugins instead of the normal builders to speed up the process!
*
* Because of the quick functionality, these messages are less customisable by other plugins.
*/
export class ODQuickMessage {
/**The id of this message. */
id: ODId
/**The current data of this message. */
data: Partial<ODMessageData>
constructor(id:ODValidId,data:Partial<ODMessageData>){
this.id = new ODId(id)
this.data = data
}
/**Build this message & compile it for discord.js */
async build(): Promise<ODMessageBuildResult> {
//create the discord.js message
const componentArray: discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder>[] = []
let currentRow: discord.ActionRowBuilder<discord.MessageActionRowComponentBuilder> = new discord.ActionRowBuilder()
this.data.components?.forEach((c) => {
//return when component crashed
if (c.component == null) return
else if (c.component == "\n"){
//create new current row when required
if (currentRow.components.length > 0){
componentArray.push(currentRow)
currentRow = new discord.ActionRowBuilder()
}
}else if (c.component instanceof discord.BaseSelectMenuBuilder){
//push current row when not empty
if (currentRow.components.length > 0){
componentArray.push(currentRow)
currentRow = new discord.ActionRowBuilder()
}
currentRow.addComponents(c.component)
//create new current row after dropdown
componentArray.push(currentRow)
currentRow = new discord.ActionRowBuilder()
}else{
//push button to current row
currentRow.addComponents(c.component)
}
//create new row when 5 rows in length
if (currentRow.components.length == 5){
componentArray.push(currentRow)
currentRow = new discord.ActionRowBuilder()
}
})
//push final row to array
if (currentRow.components.length > 0) componentArray.push(currentRow)
const filteredEmbeds = (this.data.embeds?.map((e) => e.embed).filter((e) => e instanceof discord.EmbedBuilder) as discord.EmbedBuilder[]) ?? []
const filteredFiles = (this.data.files?.map((f) => f.file).filter((f) => f instanceof discord.AttachmentBuilder) as discord.AttachmentBuilder[]) ?? []
const message : discord.MessageCreateOptions = {
content:this.data.content ?? "",
poll:this.data.poll ?? undefined,
embeds:filteredEmbeds,
components:componentArray,
files:filteredFiles
}
let result = {id:this.id,message,ephemeral:this.data.ephemeral ?? false}
Object.assign(result.message,this.data.additionalOptions)
return result
}
}
/**## ODModalManager `class`
* This is an Open Ticket modal manager.
*
+128 -43
View File
@@ -82,6 +82,10 @@ export class ODCheckerManager extends ODManager<ODChecker> {
messages:final
}
}
/**Create temporary and unlisted `ODConfig`, `ODChecker` & `ODCheckerStorage` classes. This will help you use a `ODCheckerStructure` validator without officially registering it in `opendiscord.checkers`. */
createTemporaryCheckerEnvironment(){
return new ODChecker("opendiscord:temporary-environment",new ODCheckerStorage(),0,new ODConfig("opendiscord:temporary-environment",{}),new ODCheckerStructure("opendiscord:temporary-environment",{}))
}
}
/**## ODCheckerStorage `class`
@@ -289,6 +293,16 @@ export class ODCheckerFunctionManager extends ODManager<ODCheckerFunction> {
*/
export type ODCheckerLocationTrace = (string|number)[]
/**## ODCheckerOptions `interface`
* This interface contains all optional properties to customise in the `ODChecker` class.
*/
export interface ODCheckerOptions {
/**The name of this config in the Interactive Setup CLI. */
cliDisplayName?:string
/**The description of this config in the Interactive Setup CLI. */
cliDisplayDescription?:string
}
/**## ODChecker `class`
* This is an Open Ticket config checker.
*
@@ -310,15 +324,29 @@ export class ODChecker extends ODManagerData {
messages: ODCheckerMessage[] = []
/**Temporary storage for the quit status from the check() method (not recommended to use) */
quit: boolean = false
/**All additional properties of this config checker. */
options: ODCheckerOptions
constructor(id:ODValidId, storage: ODCheckerStorage, priority:number, config:ODConfig, structure: ODCheckerStructure){
constructor(id:ODValidId, storage: ODCheckerStorage, priority:number, config:ODConfig, structure:ODCheckerStructure, options?:ODCheckerOptions){
super(id)
this.storage = storage
this.priority = priority
this.config = config
this.structure = structure
this.options = options ?? {}
}
/**Get a human-readable number string. */
#ordinalNumber(num:number){
const i = Math.abs(Math.round(num))
const cent = i % 100
if (cent >= 10 && cent <= 20) return i+'th'
const dec = i % 10
if (dec === 1) return i+'st'
if (dec === 2) return i+'nd'
if (dec === 3) return i+'rd'
return i+'th'
}
/**Run this checker. Returns all errors*/
check(): ODCheckerResult {
this.messages = []
@@ -330,12 +358,12 @@ export class ODChecker extends ODManagerData {
messages:this.messages
}
}
/**Create a string from the location trace (path)*/
/**Create a string from the location trace/path in a human readable format. */
locationTraceToString(trace:ODCheckerLocationTrace){
const final: ODCheckerLocationTrace = []
trace.forEach((t) => {
if (typeof t == "number"){
final.push(`:${t}`)
final.push(`:(${this.#ordinalNumber(t+1)})`)
}else{
final.push(`."${t}"`)
}
@@ -391,7 +419,15 @@ export interface ODCheckerStructureOptions {
/**Add a custom checker function. Returns `true` when valid. */
custom?:(checker:ODChecker, value:ODValidJsonType, locationTrace:ODCheckerLocationTrace, locationId:ODId, locationDocs:string|null) => boolean,
/**Set the url to the documentation of this variable. */
docs?:string
docs?:string,
/**The name of this config in the Interactive Setup CLI. */
cliDisplayName?:string
/**The description of this config in the Interactive Setup CLI. */
cliDisplayDescription?:string
/**Hide the description of this config in the Interactive Setup CLI parent view/list. */
cliHideDescriptionInParent?:boolean
/**The default value of this variable when creating it in the Interactive Setup CLI. When not specified, the user will be asked to insert a value. */
cliInitDefaultValue?:ODValidJsonType
}
/**## ODCheckerStructure `class`
@@ -426,7 +462,13 @@ export class ODCheckerStructure {
*/
export interface ODCheckerObjectStructureOptions extends ODCheckerStructureOptions {
/**Add a checker for a property in an object (can also be optional) */
children?:{key:string, checker:ODCheckerStructure, priority:number, optional:boolean}[]
children:{key:string, priority?:number, optional?:boolean, cliHideInEditMode?:boolean, checker:ODCheckerStructure}[],
/**A list of keys to skip when creating this object with the Interactive Setup CLI. The default value of these properties will be used instead. */
cliInitSkipKeys?:string[],
/**The key of a (primitive) property in this object to show the value of in the Interactive Setup CLI when listed in an array. */
cliDisplayKeyInParentArray?:string,
/**A list of additional (primitive) property keys in this object to show the value of in the Interactive Setup CLI when listed in an array. */
cliDisplayAdditionalKeysInParentArray?:string[]
}
/**## ODCheckerObjectStructure `class`
@@ -454,8 +496,8 @@ export class ODCheckerObjectStructure extends ODCheckerStructure {
//sort children
if (typeof this.options.children == "undefined") return super.check(checker,value,locationTrace)
const sortedChildren = this.options.children.sort((a,b) => {
if (a.priority < b.priority) return -1
else if (a.priority > b.priority) return 1
if ((a.priority ?? 0) < (b.priority ?? 0)) return -1
else if ((a.priority ?? 0) > (b.priority ?? 0)) return 1
else return 0
})
@@ -497,10 +539,28 @@ export interface ODCheckerStringStructureOptions extends ODCheckerStructureOptio
endsWith?:string,
/**This string needs to contain ... */
contains?:string,
/**This string is not allowed to contain ... */
invertedContains?:string,
/**You need to choose between ... */
choices?:string[],
/**This string needs to be in lowercase. */
lowercaseOnly?:boolean,
/**This string needs to be in uppercase. */
uppercaseOnly?:boolean,
/**This string shouldn't contain any special characters (allowed: A-Z, a-z, 0-9, space, a few punctuation marks, ...). */
noSpecialCharacters?:boolean,
/**Do not allow any spaces in this string. */
withoutSpaces?:boolean,
/**Give a warning when a sentence doesn't start with a capital letter. Or require every word to start with a capital letter. (Ignores numbers, unicode characters, ...) */
capitalLetterWarning?:false|"sentence"|"word"
/**Give a warning when a sentence doesn't end with a punctuation letter (.,?!) */
punctuationWarning?:boolean
/**The string needs to match this regex */
regex?:RegExp
regex?:RegExp,
/**Provide an optional list for autocomplete when using the Interactive Setup CLI. Defaults to the `choices` option. */
cliAutocompleteList?:string[],
/**Dynamically provide a list for autocomplete items when using the Interactive Setup CLI. */
cliAutocompleteFunc?:() => Promise<string[]|null>
}
/**## ODCheckerStringStructure `class`
@@ -540,13 +600,35 @@ export class ODCheckerStringStructure extends ODCheckerStructure {
}else if (typeof this.options.contains != "undefined" && !value.includes(this.options.contains)){
checker.createMessage("opendiscord:string-contains","error",`This string needs to contain "${this.options.contains}"!`,lt,null,[`"${this.options.contains}"`],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.invertedContains != "undefined" && value.includes(this.options.invertedContains)){
checker.createMessage("opendiscord:string-inverted-contains","error",`This string is not allowed to contain "${this.options.invertedContains}"!`,lt,null,[`"${this.options.invertedContains}"`],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.choices != "undefined" && !this.options.choices.includes(value)){
checker.createMessage("opendiscord:string-choices","error",`This string can only be one of the following values: "${this.options.choices.join(`", "`)}"!`,lt,null,[`"${this.options.choices.join(`", "`)}"`],this.id,(this.options.docs ?? null))
return false
}else if (this.options.lowercaseOnly && value !== value.toLowerCase()){
checker.createMessage("opendiscord:string-lowercase","error",`This string must be written in lowercase only!`,lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (this.options.uppercaseOnly && value !== value.toUpperCase()){
checker.createMessage("opendiscord:string-uppercase","error",`This string must be written in uppercase only!`,lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (this.options.noSpecialCharacters && !/^[A-Za-z0-9 ]*$/.test(value)){
checker.createMessage("opendiscord:string-special-characters","error",`This string is not allowed to contain any special characters! (a-z, 0-9 & space only)`,lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (this.options.withoutSpaces && value.includes(" ")){
checker.createMessage("opendiscord:string-no-spaces","error",`This string is not allowed to contain spaces!`,lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.regex != "undefined" && !this.options.regex.test(value)){
checker.createMessage("opendiscord:string-regex","error","This string is invalid!",lt,null,[],this.id,(this.options.docs ?? null))
return false
}else return super.check(checker,value,locationTrace)
}else{
//warnings
if ((this.options.capitalLetterWarning == "word" && !value.split(" ").every((word) => word.length == 0 || /^[^a-z].*/.test(word)))) checker.createMessage("opendiscord:string-capital-word","warning",`It's recommended that each word in this string starts with a capital letter!`,lt,null,[],this.id,(this.options.docs ?? null))
if ((this.options.capitalLetterWarning == "sentence" && !value.split(/ *[.?!] */).every((sentence) => sentence.length == 0 || /^[^a-z].*/.test(sentence)))) checker.createMessage("opendiscord:string-capital-sentence","warning",`It looks like some sentences in this string don't start with a capital letter!`,lt,null,[],this.id,(this.options.docs ?? null))
if (this.options.punctuationWarning && value.length > 0 && (!value.endsWith(".") && !value.endsWith("?") && !value.endsWith("!") && !value.endsWith("'") && !value.endsWith('"') && !value.endsWith(",") && !value.endsWith(";") && !value.endsWith(":") && !value.endsWith("="))) checker.createMessage("opendiscord:string-punctuation","warning",`It looks like the sentence in this string doesn't end with a punctuation mark!`,lt,null,[],this.id,(this.options.docs ?? null))
return super.check(checker,value,locationTrace)
}
}
}
@@ -554,6 +636,8 @@ export class ODCheckerStringStructure extends ODCheckerStructure {
* This interface has the options for `ODCheckerNumberStructure`!
*/
export interface ODCheckerNumberStructureOptions extends ODCheckerStructureOptions {
/**Is `NaN` (not a number) allowed? (`false` by default) */
nanAllowed?:boolean
/**The minimum length of this number */
minLength?:number,
/**The maximum length of this number */
@@ -576,6 +660,8 @@ export interface ODCheckerNumberStructureOptions extends ODCheckerStructureOptio
endsWith?:string,
/**This number needs to contain ... */
contains?:string,
/**This number is not allowed to contain ... */
invertedContains?:string,
/**You need to choose between ... */
choices?:number[],
/**Are numbers with a decimal value allowed? */
@@ -610,6 +696,9 @@ export class ODCheckerNumberStructure extends ODCheckerStructure {
if (typeof value != "number"){
checker.createMessage("opendiscord:invalid-type","error","This property needs to be the type: number!",lt,null,["number"],this.id,(this.options.docs ?? null))
return false
}else if (!this.options.nanAllowed && isNaN(value)){
checker.createMessage("opendiscord:number-nan","error",`This number can't be NaN (Not A Number)!`,lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.minLength != "undefined" && value.toString().length < this.options.minLength){
checker.createMessage("opendiscord:number-too-short","error",`This number can't be shorter than ${this.options.minLength} characters!`,lt,null,[this.options.minLength.toString()],this.id,(this.options.docs ?? null))
return false
@@ -641,19 +730,22 @@ export class ODCheckerNumberStructure extends ODCheckerStructure {
}else if (typeof this.options.contains != "undefined" && !value.toString().includes(this.options.contains)){
checker.createMessage("opendiscord:number-contains","error",`This number needs to contain "${this.options.contains}"!`,lt,null,[`"${this.options.contains}"`],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.invertedContains != "undefined" && value.toString().includes(this.options.invertedContains)){
checker.createMessage("opendiscord:number-inverted-contains","error",`This number is not allowed to contain "${this.options.invertedContains}"!`,lt,null,[`"${this.options.invertedContains}"`],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.choices != "undefined" && !this.options.choices.includes(value)){
checker.createMessage("opendiscord:number-choices","error",`This number can only be one of the following values: "${this.options.choices.join(`", "`)}"!`,lt,null,[`"${this.options.choices.join(`", "`)}"`],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.floatAllowed != "undefined" && !this.options.floatAllowed && (value % 1) !== 0){
checker.createMessage("opendiscord:number-float","error","This number can't be a decimal!",lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.negativeAllowed != "undefined" && value < 0){
}else if (typeof this.options.negativeAllowed != "undefined" && !this.options.negativeAllowed && value < 0){
checker.createMessage("opendiscord:number-negative","error","This number can't be negative!",lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.positiveAllowed != "undefined" && value > 0){
}else if (typeof this.options.positiveAllowed != "undefined" && !this.options.positiveAllowed && value > 0){
checker.createMessage("opendiscord:number-positive","error","This number can't be positive!",lt,null,[],this.id,(this.options.docs ?? null))
return false
}else if (typeof this.options.zeroAllowed != "undefined" && value === 0){
}else if (typeof this.options.zeroAllowed != "undefined" && !this.options.zeroAllowed && value === 0){
checker.createMessage("opendiscord:number-zero","error","This number can't be zero!",lt,null,[],this.id,(this.options.docs ?? null))
return false
}else return super.check(checker,value,locationTrace)
@@ -718,7 +810,9 @@ export interface ODCheckerArrayStructureOptions extends ODCheckerStructureOption
/**Allow double values (only for `string`, `number` & `boolean`) */
allowDoubles?:boolean
/**Only allow these types in the array (for multi-type propertyCheckers) */
allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[],
/**The name of the properties inside this array. Used in the GUI of the Interactive Setup CLI. */
cliDisplayPropertyName?:string
}
/**## ODCheckerArrayStructure `class`
@@ -863,15 +957,15 @@ export interface ODCheckerTypeSwitchStructureOptions extends ODCheckerStructureO
/**A checker when the property is a boolean */
boolean?:ODCheckerBooleanStructure,
/**A checker when the property is null */
null?:ODCheckerStructure,
null?:ODCheckerNullStructure,
/**A checker when the property is an array */
array?:ODCheckerStructure,
array?:ODCheckerArrayStructure,
/**A checker when the property is an object */
object?:ODCheckerObjectStructure,
/**A checker when the property is something else */
other?:ODCheckerStructure,
/**A list of allowed types */
allowedTypes?:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
allowedTypes:("string"|"number"|"boolean"|"null"|"array"|"object"|"other")[]
}
/**## ODCheckerTypeSwitchStructure `class`
@@ -925,9 +1019,9 @@ export class ODCheckerTypeSwitchStructure extends ODCheckerStructure {
*/
export interface ODCheckerObjectSwitchStructureOptions extends ODCheckerStructureOptions {
/**An array of object checkers with their name, properties & priority. */
objects?:{
objects:{
/**The properties to match for this checker to be used. */
properties:{key:string, value:any}[],
properties:{key:string, value:boolean|string|number}[],
/**The name for this object type (used in rendering) */
name:string,
/**The higher the priority, the earlier this checker will be tested. */
@@ -993,11 +1087,11 @@ export class ODCheckerObjectSwitchStructure extends ODCheckerStructure {
*/
export interface ODCheckerEnabledObjectStructureOptions extends ODCheckerStructureOptions {
/**The name of the property to match the `enabledValue`. */
property?:string,
/**The value of the property to be enabled. Defaults to `true` */
enabledValue?:any,
property:string,
/**The value of the property to be enabled. (e.g. `true`) */
enabledValue:boolean|string|number,
/**The object checker to use once the property has been matched. */
checker?:ODCheckerObjectStructure
checker:ODCheckerObjectStructure
}
/**## ODCheckerEnabledObjectStructure `class`
@@ -1387,38 +1481,29 @@ export class ODCheckerCustomStructure_UniqueIdArray extends ODCheckerArrayStruct
/**The scope to push unique ids when used in this array! */
readonly usedScope: string|null
constructor(id:ODValidId, source:string, scope:string, usedScope?:string, options?:ODCheckerArrayStructureOptions){
constructor(id:ODValidId, source:string, scope:string, usedScope?:string, options?:ODCheckerArrayStructureOptions, idOptions?:Omit<ODCheckerStringStructureOptions,"minLength"|"custom">){
//add premade custom structure checker
const newOptions = options ?? {}
newOptions.custom = (checker,value,locationTrace,locationId,locationDocs) => {
const lt = checker.locationTraceDeref(locationTrace)
newOptions.propertyChecker = new ODCheckerStringStructure("opendiscord:unique-id",{...(idOptions ?? {}),minLength:1,custom:(checker,value,locationTrace,locationId,locationDocs) => {
if (typeof value != "string") return false
const localLt = checker.locationTraceDeref(locationTrace)
localLt.pop()
if (!Array.isArray(value)) return false
const uniqueArray: string[] = (checker.storage.get(source,scope) === null) ? [] : checker.storage.get(source,scope)
let localQuit = false
value.forEach((id,index) => {
const localLt = checker.locationTraceDeref(lt)
localLt.push(index)
if (typeof id != "string") {
//id isn't a string
checker.createMessage("opendiscord:id-invalid-type", "error", "This id needs to be a string!", localLt, null, [], this.id, (this.options.docs ?? null))
localQuit = true
}else if (uniqueArray.includes(id)) {
const uniqueArray: string[] = checker.storage.get(source,scope) ?? []
if (uniqueArray.includes(value)){
//exists
if (usedScope){
const current: string[] = checker.storage.get(source,usedScope) ?? []
current.push(id)
current.push(value)
checker.storage.set(source,usedScope,current)
}
return true
}else{
//doesn't exist
checker.createMessage("opendiscord:id-non-existent","error",`The id "${id}" doesn't exist!`,localLt,null,[`"${id}"`],this.id,(this.options.docs ?? null))
localQuit = true
}
})
return !localQuit
checker.createMessage("opendiscord:id-non-existent","error",`The id "${value}" doesn't exist!`,localLt,null,[`"${value}"`],locationId,locationDocs)
return false
}
}})
super(id,newOptions)
this.source = source
this.scope = scope
+472 -17
View File
@@ -76,12 +76,18 @@ export class ODClientManager {
slashCommands: ODSlashCommandManager
/**The text command manager is responsible for all text commands & their events inside the bot. */
textCommands: ODTextCommandManager
/**The context menu manager is responsible for all context menus & their events inside the bot. */
contextMenus: ODContextMenuManager
/**The autocomplete manager is responsible for all autocomplete events inside the bot. */
autocompletes: ODAutocompleteManager
constructor(debug:ODDebugger){
this.#debug = debug
this.activity = new ODClientActivityManager(this.#debug,this)
this.slashCommands = new ODSlashCommandManager(this.#debug,this)
this.textCommands = new ODTextCommandManager(this.#debug,this)
this.contextMenus = new ODContextMenuManager(this.#debug,this)
this.autocompletes = new ODAutocompleteManager(this.#debug,this)
}
/**Initiate the `client` variable & add the intents & partials to the bot. */
@@ -114,7 +120,7 @@ export class ODClientManager {
this.#debug.debug("Created client with permissions: "+this.permissions.join(", "))
}
/**Get all servers the bot is part of. */
getGuilds(){
async getGuilds(): Promise<discord.Guild[]> {
if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
@@ -135,19 +141,21 @@ export class ODClientManager {
})
}
}
/**Log-in with a discord auth token. */
login(): Promise<boolean> {
/**Log-in with a discord auth token. Rejects returns `false` using 'softErrors' on failure. */
login(softErrors?:boolean): Promise<boolean> {
return new Promise(async (resolve,reject) => {
if (!this.initiated) reject("Client isn't initiated yet!")
if (!this.token) reject("Client doesn't have a token!")
try {
this.client.once("ready",async () => {
this.client.once("clientReady",async () => {
this.ready = true
//set slashCommandManager to client applicationCommandManager
if (!this.client.application) throw new ODSystemError("Couldn't get client application! Unable to register slash commands!")
//set slashCommandManager & contextMenuManager to client applicationCommandManager
if (!this.client.application) throw new ODSystemError("Couldn't get client application for slashCommand & contextMenu managers!")
this.slashCommands.commandManager = this.client.application.commands
this.contextMenus.commandManager = this.client.application.commands
this.autocompletes.commandManager = this.client.application.commands
if (this.readyListener) await this.readyListener()
resolve(true)
@@ -158,7 +166,8 @@ export class ODClientManager {
this.#debug.debug("Finished discord.js client.login()")
this.loggedIn = true
}catch(err){
if (err.message.toLowerCase().includes("used disallowed intents")){
if (softErrors) return resolve(false)
else if (err.message.toLowerCase().includes("used disallowed intents")){
process.emit("uncaughtException",new ODSystemError("Used disallowed intents"))
}else if (err.message.toLowerCase().includes("tokeninvalid") || err.message.toLowerCase().includes("an invalid token was provided")){
process.emit("uncaughtException",new ODSystemError("Invalid discord bot token provided"))
@@ -326,10 +335,10 @@ export class ODClientManager {
* Possible activity types for the bot.
*/
export type ODClientActivityType = ("playing"|"listening"|"watching"|"custom"|false)
/**## ODClientPermissions `type`
/**## ODClientActivityMode `type`
* Possible activity statuses for the bot.
*/
export type ODClientActivityStatus = ("online"|"invisible"|"idle"|"dnd")
export type ODClientActivityMode = ("online"|"invisible"|"idle"|"dnd")
/**## ODClientActivityManager `class`
@@ -349,8 +358,10 @@ export class ODClientActivityManager {
type: ODClientActivityType = false
/**The current status text */
text: string = ""
/**The current status status */
status: ODClientActivityStatus = "online"
/**The current status mode */
mode: ODClientActivityMode = "online"
/**Additional state text */
state: string = ""
/**The timer responsible for refreshing the status. Stop it using `clearInterval(interval)` */
interval?: NodeJS.Timeout
@@ -365,10 +376,11 @@ export class ODClientActivityManager {
}
/**Update the status. When already initiated, it can take up to 10min to see the updated status in discord. */
setStatus(type:ODClientActivityType, text:string, status:ODClientActivityStatus, forceUpdate?:boolean){
setStatus(type:ODClientActivityType, text:string, mode:ODClientActivityMode, state:string, forceUpdate?:boolean){
this.type = type
this.text = text
this.status = status
this.mode = mode
this.state = state
if (forceUpdate) this.#updateClientActivity(this.type,this.text)
}
@@ -395,10 +407,10 @@ export class ODClientActivityManager {
this.manager.client.user.setPresence({
activities:[{
type:this.#getStatusTypeEnum(type),
state:undefined,
state:this.state ? this.state : undefined,
name:text,
}],
status:this.status
status:this.mode
})
}
/**Get the enum that links to the correct type */
@@ -515,14 +527,15 @@ export interface ODSlashCommandUniversalCommand {
* The builder for slash commands. Here you can add options to the command.
*/
export interface ODSlashCommandBuilder extends discord.ChatInputApplicationCommandData {
/**@deprecated `dmPermission` is deprecated. Use `context` instead! (Not using contexts might result in the slash command being re-registered on every startup!) */
dmPermission?:boolean
/**This field is required in Open Ticket for future compatibility. */
integrationTypes:discord.ApplicationIntegrationType[],
/**This field is required in Open Ticket for future compatibility. */
contexts:discord.InteractionContextType[]
}
/**## ODSlashCommandComparator `class`
* A utility class to compare existing slash commands with newly registered ones.
*/
export class ODSlashCommandComparator {
/**Convert a `discord.ApplicationCommandOptionChoiceData<string>` to a universal Open Ticket slash command option choice object for comparison. */
#convertOptionChoice(choice:discord.ApplicationCommandOptionChoiceData<string>): ODSlashCommandUniversalOptionChoice {
@@ -1790,3 +1803,445 @@ export class ODTextCommandManager extends ODManager<ODTextCommand> {
else return super.add(data,overwrite)
}
}
/**## ODContextMenuUniversalMenu `interface`
* A universal template for a context menu.
*
* Why universal? Both **existing context menus** & **unregistered templates** can be converted to this type.
*/
export interface ODContextMenuUniversalMenu {
/**The type of this context menu. (required => `Message`|`User`) */
type:discord.ApplicationCommandType.Message|discord.ApplicationCommandType.User,
/**The name of this context menu. */
name:string,
/**All localized names of this context menu. */
nameLocalizations:readonly ODSlashCommandUniversalTranslation[],
/**The id of the guild this context menu is registered in. */
guildId:string|null,
/**Is this context menu for 18+ users only? */
nsfw:boolean,
/**A bitfield of the user permissions required to use this context menu. */
defaultMemberPermissions:bigint,
/**Is this context menu available in DM? */
dmPermission:boolean,
/**A list of contexts where you can install this context menu. */
integrationTypes:readonly discord.ApplicationIntegrationType[],
/**A list of contexts where you can use this context menu. */
contexts:readonly discord.InteractionContextType[]
}
/**## ODContextMenuBuilderMessage `interface`
* The builder for message context menus.
*/
export interface ODContextMenuBuilderMessage extends discord.MessageApplicationCommandData {
/**This field is required in Open Ticket for future compatibility. */
integrationTypes:discord.ApplicationIntegrationType[],
/**This field is required in Open Ticket for future compatibility. */
contexts:discord.InteractionContextType[]
}
/**## ODContextMenuBuilderUser `interface`
* The builder for user context menus.
*/
export interface ODContextMenuBuilderUser extends discord.UserApplicationCommandData {
/**This field is required in Open Ticket for future compatibility. */
integrationTypes:discord.ApplicationIntegrationType[],
/**This field is required in Open Ticket for future compatibility. */
contexts:discord.InteractionContextType[]
}
/**## ODContextMenuBuilderUser `interface`
* The builder for context menus.
*/
export type ODContextMenuBuilder = (ODContextMenuBuilderMessage|ODContextMenuBuilderUser)
/**## ODContextMenuComparator `class`
* A utility class to compare existing context menu's with newly registered ones.
*/
export class ODContextMenuComparator {
/**Convert a `ODContextMenuBuilder` to a universal Open Ticket context menu object for comparison. */
convertBuilder(builder:ODContextMenuBuilder,guildId:string|null): ODContextMenuUniversalMenu|null {
if (builder.type != discord.ApplicationCommandType.Message && builder.type != discord.ApplicationCommandType.User) return null
const nameLoc = builder.nameLocalizations ?? {}
return {
type:builder.type,
name:builder.name,
nameLocalizations:Object.keys(nameLoc).map((key) => {return {language:key as `${discord.Locale}`,value:nameLoc[key]}}),
guildId:guildId,
nsfw:builder.nsfw ?? false,
defaultMemberPermissions:discord.PermissionsBitField.resolve(builder.defaultMemberPermissions ?? ["ViewChannel"]),
dmPermission:(builder.contexts && builder.contexts.includes(discord.InteractionContextType.BotDM)) ?? false,
integrationTypes:builder.integrationTypes ?? [discord.ApplicationIntegrationType.GuildInstall],
contexts:builder.contexts ?? []
}
}
/**Convert a `discord.ApplicationCommand` to a universal Open Ticket context menu object for comparison. */
convertMenu(cmd:discord.ApplicationCommand): ODContextMenuUniversalMenu|null {
if (cmd.type != discord.ApplicationCommandType.Message && cmd.type != discord.ApplicationCommandType.User) return null
const nameLoc = cmd.nameLocalizations ?? {}
return {
type:cmd.type,
name:cmd.name,
nameLocalizations:Object.keys(nameLoc).map((key) => {return {language:key as `${discord.Locale}`,value:nameLoc[key]}}),
guildId:cmd.guildId,
nsfw:cmd.nsfw,
defaultMemberPermissions:discord.PermissionsBitField.resolve(cmd.defaultMemberPermissions ?? ["ViewChannel"]),
dmPermission:(cmd.contexts && cmd.contexts.includes(discord.InteractionContextType.BotDM)) ? true : false,
integrationTypes:cmd.integrationTypes ?? [discord.ApplicationIntegrationType.GuildInstall],
contexts:cmd.contexts ?? []
}
}
/**Returns `true` when the 2 context menus are the same. */
compare(ctxA:ODContextMenuUniversalMenu,ctxB:ODContextMenuUniversalMenu): boolean {
if (ctxA.name != ctxB.name) return false
if (ctxA.type != ctxB.type) return false
if (ctxA.nsfw != ctxB.nsfw) return false
if (ctxA.guildId != ctxB.guildId) return false
if (ctxA.dmPermission != ctxB.dmPermission) return false
if (ctxA.defaultMemberPermissions != ctxB.defaultMemberPermissions) return false
//nameLocalizations
if (ctxA.nameLocalizations.length != ctxB.nameLocalizations.length) return false
if (!ctxA.nameLocalizations.every((nameA) => {
const nameB = ctxB.nameLocalizations.find((nameB) => nameB.language == nameA.language)
if (!nameB || nameA.value != nameB.value) return false
else return true
})) return false
//contexts
if (ctxA.contexts.length != ctxB.contexts.length) return false
if (!ctxA.contexts.every((contextA) => {
return ctxB.contexts.includes(contextA)
})) return false
//integrationTypes
if (ctxA.integrationTypes.length != ctxB.integrationTypes.length) return false
if (!ctxA.integrationTypes.every((integrationA) => {
return ctxB.integrationTypes.includes(integrationA)
})) return false
return true
}
}
/**## ODContextMenuInteractionCallback `type`
* Callback for the context menu interaction listener.
*/
export type ODContextMenuInteractionCallback = (interaction:discord.ContextMenuCommandInteraction,cmd:ODContextMenu) => void
/**## ODContextMenuRegisteredResult `type`
* The result which will be returned when getting all (un)registered user context menu's from the manager.
*/
export type ODContextMenuRegisteredResult = {
/**A list of all registered context menus. */
registered:{
/**The instance (`ODContextMenu`) from this context menu. */
instance:ODContextMenu,
/**The universal object/template/builder of this context menu. */
menu:ODContextMenuUniversalMenu,
/**Does this context menu require an update? */
requiresUpdate:boolean
}[],
/**A list of all unregistered context menus. */
unregistered:{
/**The instance (`ODContextMenu`) from this context menu. */
instance:ODContextMenu,
/**The universal object/template/builder of this context menu. */
menu:null,
/**Does this context menu require an update? */
requiresUpdate:true
}[],
/**A list of all unused context menus (not found in `ODContextMenuManager`). */
unused:{
/**The instance (`ODContextMenu`) from this context menu. */
instance:null,
/**The universal object/template/builder of this context menu. */
menu:ODContextMenuUniversalMenu,
/**Does this context menu require an update? */
requiresUpdate:false
}[]
}
/**## ODContextMenuManager `class`
* This is an Open Ticket client context menu manager.
*
* It's responsible for managing all the context interactions from the client.
*
* Here, you can add & remove context interactions & the bot will do the (de)registering.
*/
export class ODContextMenuManager extends ODManager<ODContextMenu> {
/**Alias to Open Ticket debugger. */
#debug: ODDebugger
/**Refrerence to discord.js client. */
manager: ODClientManager
/**Discord.js application commands manager. */
commandManager: discord.ApplicationCommandManager|null
/**Collection of all interaction listeners. */
#interactionListeners: {name:string|RegExp, callback:ODContextMenuInteractionCallback}[] = []
/**Set the soft limit for maximum amount of listeners. A warning will be shown when there are more listeners than this limit. */
listenerLimit: number = 100
/**A utility class used to compare 2 context menus with each other. */
comparator: ODContextMenuComparator = new ODContextMenuComparator()
constructor(debug:ODDebugger, manager:ODClientManager){
super(debug,"context menu")
this.#debug = debug
this.manager = manager
this.commandManager = (manager.client.application) ? manager.client.application.commands : null
}
/**Get all registered & unregistered message context menu commands. */
async getAllRegisteredMenus(guildId?:string): Promise<ODContextMenuRegisteredResult> {
if (!this.commandManager) throw new ODSystemError("Couldn't get client application to register message context menus!")
const menus = (await this.commandManager.fetch({guildId})).toJSON()
const registered: {instance:ODContextMenu, menu:ODContextMenuUniversalMenu, requiresUpdate:boolean}[] = []
const unregistered: {instance:ODContextMenu, menu:null, requiresUpdate:true}[] = []
const unused: {instance:null, menu:ODContextMenuUniversalMenu, requiresUpdate:false}[] = []
await this.loopAll((instance) => {
if (guildId && instance.guildId != guildId) return
const index = menus.findIndex((menu) => menu.name == instance.name)
const menu = menus[index]
menus.splice(index,1)
if (menu){
//menu is registered (and may need to be updated)
const universalBuilder = this.comparator.convertBuilder(instance.builder,instance.guildId)
const universalMenu = this.comparator.convertMenu(menu)
//menu is not of the type 'message'|'user'
if (!universalBuilder || !universalMenu) return
const didChange = !this.comparator.compare(universalBuilder,universalMenu)
const requiresUpdate = didChange || (instance.requiresUpdate ? instance.requiresUpdate(universalMenu) : false)
registered.push({instance,menu:universalMenu,requiresUpdate})
//menu is not registered
}else unregistered.push({instance,menu:null,requiresUpdate:true})
})
menus.forEach((menu) => {
//menu does not exist in the manager (only append to unused when type == 'message'|'user')
const universalCmd = this.comparator.convertMenu(menu)
if (!universalCmd) return
unused.push({instance:null,menu:universalCmd,requiresUpdate:false})
})
return {registered,unregistered,unused}
}
/**Create all context menus that are not registered yet.*/
async createNewMenus(instances:ODContextMenu[],progress?:ODManualProgressBar){
if (!this.manager.ready) throw new ODSystemError("Client isn't ready yet! Unable to register context menus!")
if (instances.length > 0 && progress){
progress.max = instances.length
progress.start()
}
for (const instance of instances){
await this.createMenu(instance)
this.#debug.debug("Created new context menu",[
{key:"id",value:instance.id.value},
{key:"name",value:instance.name},
{key:"type",value:(instance.builder.type == discord.ApplicationCommandType.Message) ? "message-context" : "user-context"}
])
if (progress) progress.increase(1)
}
}
/**Update all context menus that are already registered. */
async updateExistingMenus(instances:ODContextMenu[],progress?:ODManualProgressBar){
if (!this.manager.ready) throw new ODSystemError("Client isn't ready yet! Unable to register context menus!")
if (instances.length > 0 && progress){
progress.max = instances.length
progress.start()
}
for (const instance of instances){
await this.createMenu(instance)
this.#debug.debug("Updated existing context menu",[
{key:"id",value:instance.id.value},
{key:"name",value:instance.name},
{key:"type",value:(instance.builder.type == discord.ApplicationCommandType.Message) ? "message-context" : "user-context"}
])
if (progress) progress.increase(1)
}
}
/**Remove all context menus that are registered but unused by Open Ticket. */
async removeUnusedMenus(instances:ODContextMenuUniversalMenu[],guildId?:string,progress?:ODManualProgressBar){
if (!this.manager.ready) throw new ODSystemError("Client isn't ready yet! Unable to register context menus!")
if (!this.commandManager) throw new ODSystemError("Couldn't get client application to register context menus!")
if (instances.length > 0 && progress){
progress.max = instances.length
progress.start()
}
const menus = await this.commandManager.fetch({guildId})
for (const instance of instances){
const menu = menus.find((menu) => menu.name == instance.name)
if (menu){
try {
await menu.delete()
this.#debug.debug("Removed existing context menu",[
{key:"name",value:menu.name},
{key:"guildId",value:guildId ?? "/"},
{key:"type",value:(instance.type == discord.ApplicationCommandType.Message) ? "message-context" : "user-context"}
])
}catch(err){
process.emit("uncaughtException",err)
throw new ODSystemError("Failed to delete context menu '"+menu.name+"'!")
}
}
if (progress) progress.increase(1)
}
}
/**Create a context menu. **(SYSTEM ONLY)** => Use `ODContextMenuManager` for registering context menu's the default way! */
async createMenu(menu:ODContextMenu){
if (!this.commandManager) throw new ODSystemError("Couldn't get client application to register context menu's!")
try {
await this.commandManager.create(menu.builder,(menu.guildId ?? undefined))
}catch(err){
process.emit("uncaughtException",err)
throw new ODSystemError("Failed to register context menu '"+menu.name+"'!")
}
}
/**Start listening to the discord.js client `interactionCreate` event. */
startListeningToInteractions(){
this.manager.client.on("interactionCreate",(interaction) => {
//return when not in main server or DM
if (!this.manager.mainServer || (interaction.guild && interaction.guild.id != this.manager.mainServer.id)) return
if (!interaction.isContextMenuCommand()) return
const menu = this.getFiltered((menu) => menu.name == interaction.commandName)[0]
if (!menu) return
this.#interactionListeners.forEach((listener) => {
if (typeof listener.name == "string" && (interaction.commandName != listener.name)) return
else if (listener.name instanceof RegExp && !listener.name.test(interaction.commandName)) return
//this is a valid listener
listener.callback(interaction,menu)
})
})
}
/**Callback on interaction from one or multiple context menu's. */
onInteraction(menuName:string|RegExp, callback:ODContextMenuInteractionCallback){
this.#interactionListeners.push({
name:menuName,
callback
})
if (this.#interactionListeners.length > this.listenerLimit){
this.#debug.console.log("Possible context menu interaction memory leak detected!","warning",[
{key:"listeners",value:this.#interactionListeners.length.toString()}
])
}
}
}
/**## ODContextMenuUpdateFunction `type`
* The function responsible for updating context menu's when they already exist.
*/
export type ODContextMenuUpdateFunction = (menu:ODContextMenuUniversalMenu) => boolean
/**## ODContextMenu `class`
* This is an Open Ticket context menu.
*
* When registered, you can listen for this context menu using the `ODContextResponder`. The advantages of using this class for creating a context menu are:
* - automatic registration in discord.js
* - error reporting to the user when the bot fails to respond
* - plugins can extend this context menu
* - the bot won't re-register the context menu when it already exists (except when requested)!
*
* And more!
*/
export class ODContextMenu extends ODManagerData {
/**The discord.js builder for this context menu. */
builder: ODContextMenuBuilder
/**The id of the guild this context menu is for. `null` when not set. */
guildId: string|null
/**Function to check if the context menu requires to be updated (when it already exists). */
requiresUpdate: ODContextMenuUpdateFunction|null = null
constructor(id:ODValidId, builder:ODContextMenuBuilder, requiresUpdate?:ODContextMenuUpdateFunction, guildId?:string){
super(id)
if (builder.type != discord.ApplicationCommandType.Message && builder.type != discord.ApplicationCommandType.User) throw new ODSystemError("ApplicationCommandData is required to be the 'Message'|'User' type!")
this.builder = builder
this.guildId = guildId ?? null
this.requiresUpdate = requiresUpdate ?? null
}
/**The name of this context menu. */
get name(): string {
return this.builder.name
}
set name(name:string){
this.builder.name = name
}
}
/**## ODAutocompleteInteractionCallback `type`
* Callback for the autocomplete interaction listener.
*/
export type ODAutocompleteInteractionCallback = (interaction:discord.AutocompleteInteraction) => void
/**## ODAutocompleteManager `class`
* This is an Open Ticket client autocomplete interaction manager.
*
* It's responsible for managing all the autocomplete interactions from the client.
*/
export class ODAutocompleteManager {
/**Alias to Open Ticket debugger. */
#debug: ODDebugger
/**Refrerence to discord.js client. */
manager: ODClientManager
/**Discord.js application commands manager. */
commandManager: discord.ApplicationCommandManager|null
/**Collection of all interaction listeners. */
#interactionListeners: {cmdName:string|RegExp, optName:string|RegExp, callback:ODAutocompleteInteractionCallback}[] = []
/**Set the soft limit for maximum amount of listeners. A warning will be shown when there are more listeners than this limit. */
listenerLimit: number = 100
constructor(debug:ODDebugger, manager:ODClientManager){
this.#debug = debug
this.manager = manager
this.commandManager = (manager.client.application) ? manager.client.application.commands : null
}
/**Start listening to the discord.js client `interactionCreate` event. */
startListeningToInteractions(){
this.manager.client.on("interactionCreate",(interaction) => {
//return when not in main server or DM
if (!this.manager.mainServer || (interaction.guild && interaction.guild.id != this.manager.mainServer.id)) return
if (!interaction.isAutocomplete()) return
this.#interactionListeners.forEach((listener) => {
if (typeof listener.cmdName == "string" && (interaction.commandName != listener.cmdName)) return
else if (listener.cmdName instanceof RegExp && !listener.cmdName.test(interaction.commandName)) return
if (typeof listener.optName == "string" && (interaction.options.getFocused(true).name != listener.optName)) return
else if (listener.optName instanceof RegExp && !listener.optName.test(interaction.options.getFocused(true).name)) return
//this is a valid listener
listener.callback(interaction)
})
})
}
/**Callback on interaction from one or multiple autocompletes. */
onInteraction(cmdName:string|RegExp,optName:string|RegExp,callback:ODAutocompleteInteractionCallback){
this.#interactionListeners.push({
cmdName,optName,callback
})
if (this.#interactionListeners.length > this.listenerLimit){
this.#debug.console.log("Possible autocomplete interaction memory leak detected!","warning",[
{key:"listeners",value:this.#interactionListeners.length.toString()}
])
}
}
}
+59 -12
View File
@@ -5,6 +5,7 @@ import { ODId, ODManager, ODManagerData, ODPromiseVoid, ODSystemError, ODValidId
import nodepath from "path"
import { ODDebugger } from "./console"
import fs from "fs"
import * as fjs from "formatted-json-stringify"
/**## ODConfigManager `class`
* This is an Open Ticket config manager.
@@ -14,8 +15,17 @@ import fs from "fs"
* You can use this class to get/change/add a config file (`ODConfig`) in your plugin!
*/
export class ODConfigManager extends ODManager<ODConfig> {
/**Alias to Open Ticket debugger. */
#debug: ODDebugger
constructor(debug:ODDebugger){
super(debug,"config")
this.#debug = debug
}
add(data:ODConfig|ODConfig[],overwrite?:boolean): boolean {
if (Array.isArray(data)) data.forEach((d) => d.useDebug(this.#debug))
else data.useDebug(this.#debug)
return super.add(data,overwrite)
}
/**Init all config files. */
async init(){
@@ -42,15 +52,45 @@ export class ODConfig extends ODManagerData {
path: string = ""
/**An object/array of the entire config file! Variables inside it can be edited while the bot is running! */
data: any
/**Is this config already initiated? */
initiated: boolean = false
/**An array of listeners to run when the config gets reloaded. These are not executed on the initial loading. */
protected reloadListeners: Function[] = []
/**Alias to Open Ticket debugger. */
protected debug: ODDebugger|null = null
constructor(id:ODValidId, data:any){
super(id)
this.data = data
}
/**Use the Open Ticket debugger for logs. */
useDebug(debug:ODDebugger|null){
this.debug = debug
}
/**Init the config. */
init(): ODPromiseVoid {
//nothing
this.initiated = true
if (this.debug) this.debug.debug("Initiated config '"+this.file+"' in ODConfigManager.",[{key:"id",value:this.id.value}])
//please implement this feature in your own config extension & extend this function.
}
/**Reload the config. Be aware that this doesn't update the config data everywhere in the bot! */
reload(): ODPromiseVoid {
if (this.debug) this.debug.debug("Reloaded config '"+this.file+"' in ODConfigManager.",[{key:"id",value:this.id.value}])
//please implement this feature in your own config extension & extend this function.
}
/**Save the edited config to the filesystem. This is used by the Interactive Setup CLI. It's not recommended to use this while the bot is running. */
save(): ODPromiseVoid {
if (this.debug) this.debug.debug("Saved config '"+this.file+"' in ODConfigManager.",[{key:"id",value:this.id.value}])
//please implement this feature in your own config extension & extend this function.
}
/**Listen for a reload of this JSON file! */
onReload(cb:Function){
this.reloadListeners.push(cb)
}
/**Remove all reload listeners. Not recommended! */
removeAllReloadListeners(){
this.reloadListeners = []
}
}
@@ -65,13 +105,13 @@ export class ODConfig extends ODManagerData {
* const config = new api.ODJsonConfig("plugin-config","test.json","./plugins/testplugin/")
*/
export class ODJsonConfig extends ODConfig {
/**An array of listeners to run when the config gets reloaded. These are not executed on the initial loading. */
#reloadListeners: Function[] = []
formatter: fjs.custom.BaseFormatter
constructor(id:ODValidId, file:string, customPath?:string){
constructor(id:ODValidId, file:string, customPath?:string, formatter?:fjs.custom.BaseFormatter){
super(id,{})
this.file = (file.endsWith(".json")) ? file : file+".json"
this.path = customPath ? nodepath.join("./",customPath,this.file) : nodepath.join("./config/",this.file)
this.formatter = formatter ?? new fjs.DefaultFormatter(null,true," ")
}
/**Init the config. */
@@ -79,17 +119,20 @@ export class ODJsonConfig extends ODConfig {
if (!fs.existsSync(this.path)) throw new ODSystemError("Unable to parse config \""+nodepath.join("./",this.path)+"\", the file doesn't exist!")
try{
this.data = JSON.parse(fs.readFileSync(this.path).toString())
super.init()
}catch(err){
process.emit("uncaughtException",err)
throw new ODSystemError("Unable to parse config \""+nodepath.join("./",this.path)+"\"!")
}
}
/**Reload the JSON file. Be aware that this doesn't update classes that used individual parts of the config data! */
/**Reload the config. Be aware that this doesn't update the config data everywhere in the bot! */
reload(){
if (!this.initiated) throw new ODSystemError("Unable to reload config \""+nodepath.join("./",this.path)+"\", the file hasn't been initiated yet!")
if (!fs.existsSync(this.path)) throw new ODSystemError("Unable to reload config \""+nodepath.join("./",this.path)+"\", the file doesn't exist!")
try{
this.data = JSON.parse(fs.readFileSync(this.path).toString())
this.#reloadListeners.forEach((cb) => {
super.reload()
this.reloadListeners.forEach((cb) => {
try{
cb()
}catch(err){
@@ -101,12 +144,16 @@ export class ODJsonConfig extends ODConfig {
throw new ODSystemError("Unable to reload config \""+nodepath.join("./",this.path)+"\"!")
}
}
/**Listen for a reload of this JSON file! */
onReload(cb:Function){
this.#reloadListeners.push(cb)
/**Save the edited config to the filesystem. This is used by the Interactive Setup CLI. It's not recommended to use this while the bot is running. */
save(): ODPromiseVoid {
if (!this.initiated) throw new ODSystemError("Unable to save config \""+nodepath.join("./",this.path)+"\", the file hasn't been initiated yet!")
try{
const contents = this.formatter.stringify(this.data)
fs.writeFileSync(this.path,contents)
super.save()
}catch(err){
process.emit("uncaughtException",err)
throw new ODSystemError("Unable to save config \""+nodepath.join("./",this.path)+"\"!")
}
/**Remove all reload listeners. Not recommended! */
removeAllReloadListeners(){
this.#reloadListeners = []
}
}
+5 -3
View File
@@ -241,6 +241,8 @@ export class ODConsoleManager {
historylength = 100
/**An alias to the debugfile manager. (`otdebug.txt`) */
debugfile: ODDebugFileManager
/**Is silent mode enabled? */
silent: boolean = false
constructor(historylength:number, debugfile:ODDebugFileManager){
this.historylength = historylength
@@ -253,12 +255,12 @@ export class ODConsoleManager {
log(message:string, type?:ODConsoleMessageTypes, params?:ODConsoleMessageParam[]): void
log(message:ODConsoleMessage|ODError|string, type?:ODConsoleMessageTypes, params?:ODConsoleMessageParam[]){
if (message instanceof ODConsoleMessage){
message.render()
if (!this.silent) message.render()
if (this.debugfile) this.debugfile.writeConsoleMessage(message)
this.history.push(message)
}else if (message instanceof ODError){
message.render()
if (!this.silent) message.render()
if (this.debugfile) this.debugfile.writeErrorMessage(message)
this.history.push(message)
@@ -272,7 +274,7 @@ export class ODConsoleManager {
else if (type == "error") newMessage = new ODConsoleErrorMessage(message,params)
else newMessage = new ODConsoleSystemMessage(message,params)
newMessage.render()
if (!this.silent) newMessage.render()
if (this.debugfile) this.debugfile.writeConsoleMessage(newMessage)
this.history.push(newMessage)
}
+2 -32
View File
@@ -7,30 +7,6 @@ import nodepath from "path"
import { ODDebugger } from "./console"
import * as fjs from "formatted-json-stringify"
/////////////////////////////////////////////////////////
//TEMPORARY OPENTICKET => OPENDISCORD MIGRATION UTILITIES
/////////////////////////////////////////////////////////
/** ## ❌ Temporary function. Will be removed on full OTv4 release! */
export function TEMP_migrateDatabaseIdPrefix(id:string): string {
if (!id.startsWith("openticket:")) return id
return id.replaceAll("openticket:","opendiscord:")
}
/** ## ❌ Temporary function. Will be removed on full OTv4 release! */
export function TEMP_migrateDatabaseValuePrefix(value:string): string {
return value.replaceAll('"openticket:','"opendiscord:')
}
/** ## ❌ Temporary function. Will be removed on full OTv4 release! */
export function TEMP_migrateDatabaseStructurePrefix(structure:ODJsonDatabaseStructure): ODJsonDatabaseStructure {
return structure.map((data) => {
return {
category:TEMP_migrateDatabaseIdPrefix(data.category),
key:TEMP_migrateDatabaseIdPrefix(data.key),
value:JSON.parse(TEMP_migrateDatabaseValuePrefix(JSON.stringify(data.value)))
}
})
}
/**## ODDatabaseManager `class`
* This is an Open Ticket database manager.
*
@@ -118,10 +94,7 @@ export class ODJsonDatabase extends ODDatabase {
/**Init the database. */
init(): ODPromiseVoid {
//this.#system.getData()
//TEMPORARY!!!
const newData = TEMP_migrateDatabaseStructurePrefix(this.#system.getData())
this.#system.setData(newData)
this.#system.getData()
}
/**Set/overwrite the value of `category` & `key`. Returns `true` when overwritten!
* @example
@@ -226,10 +199,7 @@ export class ODFormattedJsonDatabase extends ODDatabase {
/**Init the database. */
init(): ODPromiseVoid {
//this.#system.getData()
//TEMPORARY!!!
const newData = TEMP_migrateDatabaseStructurePrefix(this.#system.getData())
this.#system.setData(newData)
this.#system.getData()
}
/**Set/overwrite the value of `category` & `key`. Returns `true` when overwritten!
* @example
+26
View File
@@ -13,6 +13,8 @@ export interface ODDefaults {
crashOnError:boolean,
/**Enable the system responsible for the `--debug` flag. */
debugLoading:boolean,
/**Enable the system responsible for the `--silent` flag. */
silentLoading:boolean,
/**When enabled, you're able to use the "!OPENTICKET:dump" command to send the OT debug file. This is only possible when you're the owner of the bot. */
allowDumpCommand:boolean,
/**Enable loading all Open Ticket plugins, sadly enough is only useful for the system :) */
@@ -81,6 +83,9 @@ export interface ODDefaults {
/**Load the default Open Ticket client activity initialization (& status refresh). */
clientActivityInitiating:boolean,
/**Load the default Open Ticket priority levels. */
priorityLoading:boolean,
/**Load the default Open Ticket slash commands. */
slashCommandLoading:boolean,
/**Load the default Open Ticket slash command registerer (register slash cmds in discord). */
@@ -89,6 +94,14 @@ export interface ODDefaults {
forceSlashCommandRegistration:boolean,
/**When enabled, the bot is allowed to unregister all slash commands which aren't used in Open Ticket. Disable this if you don't want to use the Open Ticket `ODSlashCommand` classes. */
allowSlashCommandRemoval:boolean,
/**Load the default Open Ticket context menus. */
contextMenuLoading:boolean,
/**Load the default Open Ticket context menu registerer (register menus in discord). */
contextMenuRegistering:boolean,
/**When enabled, the bot is forced to re-register all context menus in the server. This can be used in case of a auto-update malfunction. */
forceContextMenuRegistration:boolean,
/**When enabled, the bot is allowed to unregister all context menus which aren't used in Open Ticket. Disable this if you don't want to use the Open Ticket `ODContextMenu` classes. */
allowContextMenuRemoval:boolean,
/**Load the default Open Ticket text commands. */
textCommandLoading:boolean,
@@ -130,6 +143,10 @@ export interface ODDefaults {
dropdownRespondersLoading:boolean,
/**Load the default Open Ticket modal responders. */
modalRespondersLoading:boolean,
/**Load the default Open Ticket context menu responders. */
contextMenuRespondersLoading:boolean,
/**Load the default Open Ticket autocomplete responders. */
autocompleteRespondersLoading:boolean,
/**Set the time (in ms) before Open Ticket sends an error message when no reply is sent in a responder. */
responderTimeoutMs:number,
@@ -233,6 +250,7 @@ export class ODDefaultsManager {
errorHandling:true,
crashOnError:false,
debugLoading:true,
silentLoading:true,
allowDumpCommand:true,
pluginLoading:true,
softPluginLoading:false,
@@ -270,10 +288,16 @@ export class ODDefaultsManager {
clientActivityLoading:true,
clientActivityInitiating:true,
priorityLoading:true,
slashCommandLoading:true,
slashCommandRegistering:true,
forceSlashCommandRegistration:false,
allowSlashCommandRemoval:true,
contextMenuLoading:true,
contextMenuRegistering:true,
forceContextMenuRegistration:false,
allowContextMenuRemoval:true,
textCommandLoading:true,
questionLoading:true,
@@ -296,6 +320,8 @@ export class ODDefaultsManager {
buttonRespondersLoading:true,
dropdownRespondersLoading:true,
modalRespondersLoading:true,
contextMenuRespondersLoading:true,
autocompleteRespondersLoading:true,
responderTimeoutMs:2500,
actionsLoading:true,
+2 -5
View File
@@ -146,11 +146,8 @@ export class ODPlugin extends ODManagerData {
async execute(debug:ODDebugger,force?:boolean): Promise<boolean> {
if ((this.enabled && !this.crashed) || force){
try{
//rewrite the path to make it work on windows & unix based systems
const workingDir = process.cwd().replace(/C:\\/i,"/")
const workingPath = nodepath.join("./dist/plugins/",this.getStartFile())
const pluginPath = nodepath.join(workingDir,workingPath).split(nodepath.sep).join("/")
//import relative plugin directory path (works on windows & unix based systems)
const pluginPath = nodepath.join("../../../../plugins/",this.getStartFile())
await import(pluginPath)
debug.console.log("Plugin \""+this.id.value+"\" loaded successfully!","plugin")
this.executed = true
+303 -3
View File
@@ -5,7 +5,7 @@ import { ODId, ODManager, ODValidId, ODSystemError, ODManagerData } from "./base
import * as discord from "discord.js"
import { ODWorkerManager, ODWorkerCallback, ODWorker } from "./worker"
import { ODDebugger } from "./console"
import { ODClientManager, ODSlashCommand, ODTextCommand, ODTextCommandInteractionOption } from "./client"
import { ODClientManager, ODContextMenu, ODSlashCommand, ODTextCommand, ODTextCommandInteractionOption } from "./client"
import { ODDropdownData, ODMessageBuildResult, ODMessageBuildSentResult, ODModalBuildResult } from "./builder"
/**## ODResponderImplementation `class`
@@ -36,7 +36,7 @@ export class ODResponderImplementation<Instance,Source extends string,Params> ex
/**## ODResponderTimeoutErrorCallback `type`
* This is the callback for the responder timeout function. It will be executed when something went wrong or the action takes too much time.
*/
export type ODResponderTimeoutErrorCallback<Instance, Source extends "slash"|"text"|"button"|"dropdown"|"modal"|"other"> = (instance:Instance, source:Source) => void|Promise<void>
export type ODResponderTimeoutErrorCallback<Instance, Source extends "slash"|"text"|"button"|"dropdown"|"modal"|"other"|"context-menu"|"autocomplete"> = (instance:Instance, source:Source) => void|Promise<void>
/**## ODResponderManager `class`
* This is an Open Ticket responder manager.
@@ -61,12 +61,18 @@ export class ODResponderManager {
dropdowns: ODDropdownResponderManager
/**A manager for all modal responders. */
modals: ODModalResponderManager
/**A manager for all context menu responders. */
contextMenus: ODContextMenuResponderManager
/**A manager for all autocomplete responders. */
autocomplete: ODAutocompleteResponderManager
constructor(debug:ODDebugger, client:ODClientManager){
this.commands = new ODCommandResponderManager(debug,"command responder",client)
this.buttons = new ODButtonResponderManager(debug,"button responder",client)
this.dropdowns = new ODDropdownResponderManager(debug,"dropdown responder",client)
this.modals = new ODModalResponderManager(debug,"modal responder",client)
this.contextMenus = new ODContextMenuResponderManager(debug,"context menu responder",client)
this.autocomplete = new ODAutocompleteResponderManager(debug,"autocomplete responder",client)
}
}
@@ -1083,7 +1089,7 @@ export class ODModalResponderInstance {
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
this.didReply = true
return {success:true,message:await sent.fetch()}
}else throw new ODSystemError()
}else throw new ODSystemError("Unable to update modal interaction!")
}catch{
return {success:false,message:null}
}
@@ -1114,3 +1120,297 @@ export class ODModalResponder<Source extends string,Params> extends ODResponderI
await this.workers.executeWorkers(instance,source,params)
}
}
/**## ODContextMenuResponderManager `class`
* This is an Open Ticket context menu responder manager.
*
* It contains all Open Ticket context menu responders. These can respond to user/message context menu interactions.
*
* Using the Open Ticket responder system has a few advantages compared to vanilla discord.js:
* - plugins can extend/edit replies
* - automatically reply on error
* - independent workers (with priority)
* - fail-safe design using try-catch
* - know where the request came from!
* - And so much more!
*/
export class ODContextMenuResponderManager extends ODManager<ODContextMenuResponder<"context-menu",any>> {
/**An alias to the Open Ticket client manager. */
#client: ODClientManager
/**The callback executed when the default workers take too much time to reply. */
#timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null = null
/**The amount of milliseconds before the timeout error callback is executed. */
#timeoutMs: number|null = null
constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
super(debug,debugname)
this.#client = client
}
/**Set the message to send when the response times out! */
setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null, ms:number|null){
this.#timeoutErrorCallback = callback
this.#timeoutMs = ms
}
add(data:ODContextMenuResponder<"context-menu",any>, overwrite?:boolean){
const res = super.add(data,overwrite)
this.#client.contextMenus.onInteraction(data.match,(interaction,cmd) => {
const newData = this.get(data.id)
if (!newData) return
newData.respond(new ODContextMenuResponderInstance(interaction,cmd,this.#timeoutErrorCallback,this.#timeoutMs),"context-menu",{})
})
return res
}
}
/**## ODContextMenuResponderInstance `class`
* This is an Open Ticket context menu responder instance.
*
* An instance is an active context menu interaction. You can reply to the context menu using `reply()`.
*/
export class ODContextMenuResponderInstance {
/**The interaction which is the source of this instance. */
interaction: discord.ContextMenuCommandInteraction
/**Did a worker already reply to this instance/interaction? */
didReply: boolean = false
/**The context menu wich is the source of this instance. */
menu:ODContextMenu
/**The user who triggered this context menu. */
user: discord.User
/**The guild member who triggered this context menu. */
member: discord.GuildMember|null
/**The guild where this context menu was triggered. */
guild: discord.Guild|null
/**The channel where this context menu was triggered. */
channel: discord.TextBasedChannel
/**The target of this context menu (user or message). */
target: discord.Message|discord.User
constructor(interaction:discord.ContextMenuCommandInteraction, menu:ODContextMenu, errorCallback:ODResponderTimeoutErrorCallback<ODContextMenuResponderInstance,"context-menu">|null, timeoutMs:number|null){
if (!interaction.channel) throw new ODSystemError("ODContextMenuResponderInstance: Unable to find interaction channel!")
this.interaction = interaction
this.menu = menu
this.user = interaction.user
this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
this.guild = interaction.guild
this.channel = interaction.channel
if (interaction.isMessageContextMenuCommand()) this.target = interaction.targetMessage
else if (interaction.isUserContextMenuCommand()) this.target = interaction.targetUser
else throw new ODSystemError("ODContextMenuResponderInstance: Invalid context menu type. Should be of the type User/Message!")
setTimeout(async () => {
if (!this.didReply){
try {
if (!errorCallback){
this.reply({id:new ODId("looks-like-we-got-an-error-here"), ephemeral:true, message:{
content:":x: **Something went wrong while replying to this context menu!**"
}})
}else{
await errorCallback(this,"context-menu")
}
}catch(err){
process.emit("uncaughtException",err)
}
}
},timeoutMs ?? 2500)
}
/**Reply to this context menu. */
async reply(msg:ODMessageBuildResult): Promise<ODMessageBuildSentResult<boolean>> {
try{
const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : []
if (this.interaction.replied || this.interaction.deferred){
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
this.didReply = true
return {success:true,message:sent}
}else{
const sent = await this.interaction.reply(Object.assign(msg.message,{flags:msgFlags}))
this.didReply = true
return {success:true,message:await sent.fetch()}
}
}catch{
return {success:false,message:null}
}
}
/**Update the message of this context menu. */
async update(msg:ODMessageBuildResult): Promise<ODMessageBuildSentResult<boolean>> {
try{
const msgFlags: number[] = msg.ephemeral ? [discord.MessageFlags.Ephemeral] : []
if (this.interaction.replied || this.interaction.deferred){
const sent = await this.interaction.editReply(Object.assign(msg.message,{flags:msgFlags}))
this.didReply = true
return {success:true,message:await sent.fetch()}
}else throw new ODSystemError("Unable to update context menu interaction!")
}catch{
return {success:false,message:null}
}
}
/**Defer this context menu. */
async defer(type:"reply", ephemeral:boolean){
if (this.interaction.deferred) return false
if (type == "reply"){
const msgFlags: number[] = ephemeral ? [discord.MessageFlags.Ephemeral] : []
await this.interaction.deferReply({flags:msgFlags})
}
this.didReply = true
return true
}
/**Show a modal as reply to this context menu. */
async modal(modal:ODModalBuildResult){
this.interaction.showModal(modal.modal)
this.didReply = true
return true
}
}
/**## ODContextMenuResponder `class`
* This is an Open Ticket context menu responder.
*
* This class manages all workers which are executed when the related context menu is triggered.
*/
export class ODContextMenuResponder<Source extends string,Params> extends ODResponderImplementation<ODContextMenuResponderInstance,Source,Params> {
/**Respond to this button */
async respond(instance:ODContextMenuResponderInstance, source:Source, params:Params){
//wait for workers to finish
await this.workers.executeWorkers(instance,source,params)
}
}
/**## ODAutocompleteResponderManager `class`
* This is an Open Ticket autocomplete responder manager.
*
* It contains all Open Ticket autocomplete responders. These can respond to autocomplete interactions.
*
* Using the Open Ticket responder system has a few advantages compared to vanilla discord.js:
* - plugins can extend/edit replies
* - automatically reply on error
* - independent workers (with priority)
* - fail-safe design using try-catch
* - know where the request came from!
* - And so much more!
*/
export class ODAutocompleteResponderManager extends ODManager<ODAutocompleteResponder<"autocomplete",any>> {
/**An alias to the Open Ticket client manager. */
#client: ODClientManager
/**The callback executed when the default workers take too much time to reply. */
#timeoutErrorCallback: ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null = null
/**The amount of milliseconds before the timeout error callback is executed. */
#timeoutMs: number|null = null
constructor(debug:ODDebugger, debugname:string, client:ODClientManager){
super(debug,debugname)
this.#client = client
}
/**Set the message to send when the response times out! */
setTimeoutErrorCallback(callback:ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null, ms:number|null){
this.#timeoutErrorCallback = callback
this.#timeoutMs = ms
}
add(data:ODAutocompleteResponder<"autocomplete",any>, overwrite?:boolean){
const res = super.add(data,overwrite)
this.#client.autocompletes.onInteraction(data.cmdMatch,data.match,(interaction) => {
const newData = this.get(data.id)
if (!newData) return
newData.respond(new ODAutocompleteResponderInstance(interaction,this.#timeoutErrorCallback,this.#timeoutMs),"autocomplete",{})
})
return res
}
}
/**## ODAutocompleteResponderInstance `class`
* This is an Open Ticket autocomplete responder instance.
*
* An instance is an active autocomplete interaction. You can reply to the autocomplete using `reply()`.
*/
export class ODAutocompleteResponderInstance {
/**The interaction which is the source of this instance. */
interaction: discord.AutocompleteInteraction
/**Did a worker already respond to this instance/interaction? */
didRespond: boolean = false
/**The user who triggered this autocomplete. */
user: discord.User
/**The guild member who triggered this autocomplete. */
member: discord.GuildMember|null
/**The guild where this autocomplete was triggered. */
guild: discord.Guild|null
/**The channel where this autocomplete was triggered. */
channel: discord.TextBasedChannel
/**The target slash command option of this autocomplete. */
target: discord.AutocompleteFocusedOption
constructor(interaction:discord.AutocompleteInteraction, errorCallback:ODResponderTimeoutErrorCallback<ODAutocompleteResponderInstance,"autocomplete">|null, timeoutMs:number|null){
if (!interaction.channel) throw new ODSystemError("ODAutocompleteResponderInstance: Unable to find interaction channel!")
this.interaction = interaction
this.user = interaction.user
this.member = (interaction.member instanceof discord.GuildMember) ? interaction.member : null
this.guild = interaction.guild
this.channel = interaction.channel
this.target = interaction.options.getFocused(true)
setTimeout(async () => {
if (!this.didRespond){
process.emit("uncaughtException",new ODSystemError("Autocomplete responder instance failed to respond widthin 2.5sec!"))
}
},timeoutMs ?? 2500)
}
/**Reply to this autocomplete. */
async autocomplete(choices:(string|discord.ApplicationCommandOptionChoiceData)[]): Promise<{success:boolean}> {
const newChoices: (discord.ApplicationCommandOptionChoiceData)[] = choices.map((raw) => {
if (typeof raw == "string") return {name:raw,value:raw}
else return raw
})
try{
if (this.interaction.responded){
return {success:false}
}else{
await this.interaction.respond(newChoices)
this.didRespond = true
return {success:true}
}
}catch(err){
process.emit("uncaughtException",err)
return {success:false}
}
}
/**Reply to this autocomplete, but filter choices based on the input of the user. */
async filteredAutocomplete(choices:(string|discord.ApplicationCommandOptionChoiceData)[]): Promise<{success:boolean}> {
const newChoices: (discord.ApplicationCommandOptionChoiceData)[] = choices.map((raw) => {
if (typeof raw == "string") return {name:raw,value:raw}
else return raw
})
const filteredChoices = newChoices.filter((choice) => choice.name.startsWith(this.target.value) || choice.value.toString().startsWith(this.target.value))
return await this.autocomplete(filteredChoices)
}
}
/**## ODAutocompleteResponder `class`
* This is an Open Ticket autocomplete responder.
*
* This class manages all workers which are executed when the related autocomplete is triggered.
*/
export class ODAutocompleteResponder<Source extends string,Params> extends ODResponderImplementation<ODAutocompleteResponderInstance,Source,Params> {
/**The slash command of the autocomplete should match the following regex. */
cmdMatch: string|RegExp
constructor(id:ODValidId,cmdMatch:string|RegExp,match:string|RegExp,callback?:ODWorkerCallback<ODAutocompleteResponderInstance,Source,Params>,priority?:number,callbackId?:ODValidId){
super(id,match,callback,priority,callbackId)
this.cmdMatch = cmdMatch
}
/**Respond to this autocomplete interaction. */
async respond(instance:ODAutocompleteResponderInstance, source:Source, params:Params){
//wait for workers to finish
await this.workers.executeWorkers(instance,source,params)
}
}

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