Compare commits

..
460 Commits
Author SHA1 Message Date
Jasper 849c688e56 Open Ticket v4.2
Open Ticket v4.2
2026-05-27 18:51:20 +02:00
Jasper 6babc5e4b8 Open Ticket v4.2 --> Dev Branch
Open Ticket v4.2 --> Dev Branch
2026-05-26 20:45:35 +02:00
DJj123dj 4606ddac3c Open Ticket v4.2 🎉 2026-05-26 20:44:29 +02:00
DJj123dj e6ab09dbbd Added new v4.2 Preview Images 2026-05-26 20:16:58 +02:00
DJj123dj 39427cdbea Update updateTicketTopic.ts 2026-05-26 19:27:49 +02:00
DJj123dj adfcb1c82f Bugfixes 2026-05-25 14:34:21 +02:00
DJj123dj 6c994f6f86 (v4.2.0) Topic change (DM) logs 2026-05-25 12:28:20 +02:00
DJj123dj 4cfde9f5c4 (v4.2.0) Added all v4.2 translations 2026-05-25 12:18:23 +02:00
DJj123dj fd1e0d6bdb (v4.2.0) Added new translation sentences 2026-05-25 11:16:33 +02:00
DJj123dj e7475af694 (v4.2.0) Added /transcripts history command 2026-05-25 10:47:28 +02:00
DJj123dj 9da066e99e Add: Transfer ticket & Priority change (DM) logs 2026-05-24 22:21:01 +02:00
DJj123dj 45f15a2df6 (v4.2.0) Create ticket for another user (admin only) 2026-05-24 21:53:54 +02:00
DJj123dj 58a6bcde45 (v4.2.0) Added priority dropdown 2026-05-24 19:48:46 +02:00
DJj123dj eb44f41d3a (API) Renamed "ODCode" classes to "ODTask" (improved readability) 2026-05-24 09:47:30 +02:00
DJj123dj 66c5bc4565 Docker improvements 2026-05-23 21:52:58 +02:00
Jasper 22d4caf03e chore: update Docker setup (by MauroDruwel)
chore: update Docker setup
2026-05-23 21:04:52 +02:00
DJj123dj 77dde68edc Update build-docker.sh 2026-05-23 21:00:57 +02:00
DJj123dj c8c8a94d6f Merge branch 'dev-v4.2' into pr/208 2026-05-23 20:56:46 +02:00
DJj123dj 6c41aecfa7 Bugfixes & Improvements
Added "maximum buttons per row" in panels. Fixed reaction role reply glitching when replyOnReactionRole was disabled. Panel dropdowns now support reaction role & sub-panel options.
2026-05-23 16:45:01 +02:00
DJj123dj c64220cad4 (v4.2.0) Added sub-panels 2026-05-23 15:53:52 +02:00
DJj123dj 7f02dd5848 (v4.2.0) Added closed ticket channel emoij (#199) 2026-05-23 14:52:57 +02:00
DJj123dj c442e50cb4 (v4.2.0) Panel auto-update now uses message states 2026-05-23 14:32:22 +02:00
DJj123dj 4af9b9e17f (v4.2.0) Added 5 new question types
Added support for dropdown, radio select, checkbox select, file upload & text display question types.
2026-05-23 14:30:56 +02:00
MauroDruwel 98d13c6569 chore: update Dockerfile and rename build script for multi-architecture support 2026-05-23 11:34:54 +02:00
MauroDruwel 345a64f51a chore: update Docker Compose configuration for Open Ticket 2026-05-23 11:22:59 +02:00
MauroDruwel 45080ccd96 chore: update Docker setup
- Replace lowercase dockerfile with proper Dockerfile
- Add .dockerignore for cleaner builds
- Add build.sh script
- Update docker-compose.yml: separate volumes for config, database, plugins; set restart policy to unless-stopped; remove deprecated version field
2026-05-23 11:15:49 +02:00
DJj123dj 87c034d215 (v4.2.0) Large README overhaul (Part 2) 2026-05-22 18:21:32 +02:00
DJj123dj 1b2e38f524 Update README.md 2026-05-20 21:59:24 +02:00
DJj123dj 8f780992aa Update README.md 2026-05-20 21:57:25 +02:00
DJj123dj a28bcd4f8b (v4.2.0) Large README overhaul (Part 1) 2026-05-20 21:55:18 +02:00
Jasper a0b3aff661 Added khmer language (Cambodja) (by yuuslokrobjakkroval)
Added khmer language (Cambodja)
2026-05-20 15:20:01 +02:00
JasperAtSchool 9ca375ee2c Solved merge conflicts 2026-05-20 10:42:17 +02:00
DJj123dj 0f9ae79d49 (v4.2.0) Interactive Setup CLI Bugfixes 2026-05-19 22:20:06 +02:00
DJj123dj dd8d5a12d6 Open Ticket v4.2: Config Update (JSONC) 2026-05-17 22:28:23 +02:00
DJj123dj afd7f80b8d Code Cleanup & Improved channel rename errors 2026-05-15 22:30:24 +02:00
DJj123dj ca5faa3365 (v4.2.0) Reworked ticket channel name logic 2026-05-15 22:01:19 +02:00
DJj123dj cf9ddf58d0 (v4.2.0) Fixed all known bugs 2026-05-15 22:00:55 +02:00
DJj123dj 15ff345948 (v4.2.0) Reworked ticket category logic 2026-05-15 12:30:14 +02:00
DJj123dj 0c45a0ad6a Added labels to verifybars (improved accesibility) 2026-05-14 23:36:21 +02:00
DJj123dj e6f6aa1af4 Code cleanup & bugfixes 2026-05-14 23:13:38 +02:00
DJj123dj 1a530aae4c Finished Message States & Overhauled Verifybars 2026-05-14 21:39:50 +02:00
DJj123dj 67f4417ee6 English Improvements (Part 2) 2026-05-14 21:36:11 +02:00
DJj123dj b5e15921ef Minor Improvements + Code Cleanup 2026-05-09 11:45:52 +02:00
DJj123dj cb8afdf9a6 Message States (Part 2) + Component Modifiers 2026-05-08 21:46:13 +02:00
DJj123dj 7030b7011c Message States (Part 1) 2026-05-08 17:17:59 +02:00
DJj123dj 7dd8a57a83 Implemented ODStates (Message States) 2026-05-06 21:11:53 +02:00
DJj123dj 5763659900 Minor Bugfixes 2026-05-06 20:45:10 +02:00
DJj123dj 65334a8a25 Renamed OT Id Lists to Mappings 2026-05-04 21:44:12 +02:00
DJj123dj 4be54399a6 (v4.2.0) Improved English sentences and language 2026-05-04 21:21:44 +02:00
DJj123dj 1f9fa54344 Implemented ODComponents (Part 2) 2026-05-04 19:24:15 +02:00
DJj123dj e4b4acfd9f Rename: "source" to "origin" for improved readability 2026-05-04 19:05:58 +02:00
DJj123dj 6bfa0e0f10 Implemented ODComponents (Part 1) 2026-05-03 22:05:14 +02:00
DJj123dj 4eb9331bf4 Replaced #func() methods with TS: private func() 2026-05-03 22:04:38 +02:00
DJj123dj 10f5ae9d44 Update package.json 2026-04-19 18:16:29 +02:00
DJj123dj 27a9f04d83 Update codeLoader.ts 2026-04-19 17:46:43 +02:00
DJj123dj 600419f763 Typescript Bugfixes (Switched CJS -> MJS) 2026-04-19 17:43:59 +02:00
yuuslokrobjakkroval a18b19da66 Refactor code structure for improved readability and maintainability 2026-04-19 02:26:08 +07:00
DJj123dj 230ef70d86 Update package.json 2026-04-18 19:13:13 +02:00
DJj123dj 2d9a9b42d4 Moved many utility files to one big "tools" folder 2026-04-03 13:38:12 +02:00
DJj123dj 50c7fc5e90 (v4.2) Open Discord Framework Update (Part 3) 2026-04-03 13:27:20 +02:00
DJj123dj fa8bc0eb6c (v4.2) Open Discord Framework Update (Part 2)
Replaced all "Default" classes & types with ID mappings & improved types. Updated Interactive Setup CLI & certain events, renamed ODStat's to ODStatistics. Moved many classes, types & interfaces to the framework
2026-04-03 11:18:59 +02:00
Jasper f42b70d1b9 Update german.json (by imLudwig)
Update german.json
2026-03-24 20:21:12 +01:00
DJj123dj 4c2a617a51 Removed line ending 2026-03-24 20:19:52 +01:00
Ludwig a1198866f3 Update german.json
`s is no german plural syntax

WARNUNGS -> WARNUNGEN
2026-03-20 07:24:05 +01:00
DJj123dj fe3f2d04a9 Translation: traditional chinese (by me.october) 2026-03-10 20:50:40 +01:00
DJj123dj cb8139b8a0 (v4.2) Open Discord Framework Update (Part 1) 2026-02-16 22:12:25 +01:00
Jasper ddae7b5e46 Open Ticket v4.1.3
Open Ticket v4.1.3
2026-02-16 10:38:43 +01:00
DJj123dj 32c9a6b4f3 Update README.md 2026-02-16 10:30:16 +01:00
DJj123dj 0ad24ba89f (v4.1.3) Finished adding all missing languages 2026-02-16 10:23:14 +01:00
DJj123dj 5412416166 Updated plugin.json format (backwards compatible) 2026-02-15 13:19:48 +01:00
DJj123dj 78a937d4e9 (v4.1.3) Fixed unintended cooldown bug with modals 2026-02-15 13:13:18 +01:00
DJj123dj 5b45dc265b Readme improvements (last time 😄 ) 2026-02-14 16:12:57 +01:00
DJj123dj 4b210fe793 Readme Improvements 2026-02-14 16:11:46 +01:00
DJj123dj 8891320bdf Update README.md 2026-02-14 15:53:43 +01:00
DJj123dj 8ccfd5c4eb (v4.1.3) Added all missing translations from v4.1 2026-02-14 15:52:39 +01:00
DJj123dj 44074004e4 Update pluginLauncher.ts 2026-02-11 20:53:21 +01:00
DJj123dj 2f1c885b4f Update README.md 2026-02-11 20:43:28 +01:00
Jasper c94867280d Merge pull request #193 from SKaranjaN/feature/plugin-json-improvements
feat: improve plugin.json with multiple authors, versions, and pre-compilation dependency checking
2026-02-11 20:40:20 +01:00
DJj123dj 138f7fa2bb Small Improvements for the new plugin.json code 2026-02-11 20:37:28 +01:00
DJj123dj 465eda1c24 (v4.1.3) Updated README + plugin list 2026-02-11 19:54:06 +01:00
DJj123dj c821e7dd6d Fixed typo in LICENSE.md 2026-02-11 18:45:54 +01:00
DJj123dj f9ce496cc5 Update quickSetup.ts 2026-02-11 17:48:35 +01:00
Karanja-Stephen 3989fb5f76 refactor: simplify comments and make them less verbose 2026-02-11 10:44:51 +03:00
Karanja-Stephen 779efca39a feat: improve plugin.json with multiple authors, versions, and pre-compilation dependency checking
- Add support for multiple authors and contributors in plugin.json
- Maintain backwards compatibility with single author (string) format
- Add versions array to specify compatible Open Ticket versions (e.g., OTv4.0.x, ODv1.0.0)
- Implement version compatibility checking during plugin loading
- Read plugin.json files before TypeScript compilation to check npm dependencies
- Warn about missing npm dependencies before compilation starts
- Update example plugin to demonstrate new features

Fixes #162
2026-02-10 13:13:24 +03:00
DJj123dj 4976aa3d45 Added v4.1 Lithuanian translations (by TsgIndrius) 2026-02-08 21:17:34 +01:00
DJj123dj 9cdff2c1de Added v4.1 French translations (by @guillee3 ) 2026-02-08 13:29:04 +01:00
DJj123dj 2e29947ba5 Update README.md 2026-02-08 12:43:53 +01:00
DJj123dj 0aa3dca63b Added v4.1 Italian translations (by imperatorix_17) 2026-02-08 12:26:22 +01:00
Jasper 90851f881d Open Ticket v4.1.2
Open Ticket v4.1.2
2026-02-06 21:25:32 +01:00
DJj123dj b972c97871 Fixed: Unknown interaction when on cooldown 2026-02-06 21:16:30 +01:00
DJj123dj db94f226fb Fixed: Panel embed was replaced with cooldown error 2026-02-06 20:59:36 +01:00
DJj123dj 6a4f40d862 (v4.1.2) Readme updates & general improvements 2026-02-06 20:48:21 +01:00
DJj123dj 1f402a848e (v4.1.2) Massive permission system cleanup 2026-02-06 20:08:55 +01:00
DJj123dj 24d98aeaeb (v4.1.2) Fixed autocomplete bug with many options 2026-02-06 17:45:31 +01:00
DJj123dj b95b8f239e Fixed typescript compilation error 2026-01-01 23:00:29 +01:00
DJj123dj 488b5fce53 Fixed fetch() errors of unknown guild members 2025-12-27 10:42:10 +01:00
DJj123dj c2117a67ed Updated German translation for v4.1 2025-12-03 16:20:17 +01:00
Jasper d31be1bb57 Open Ticket v4.1.1
Open Ticket v4.1.1
2025-11-16 11:44:45 +01:00
DJj123dj 76095bea30 Update README.md 2025-11-16 11:33:16 +01:00
DJj123dj da962c6c2e Major bugfixes & improvements for v4.1 2025-11-16 11:23:15 +01:00
DJj123dj a89d5c2149 Added 7 updated translations for v4.1 2025-11-16 10:43:46 +01:00
DJj123dj 7d5313c77e Open Ticket v4.1.1 2025-11-16 10:11:56 +01:00
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
Jasper 3b3c0aff96 Open Ticket v4.0.7
Open Ticket v4.0.7
2025-06-24 10:46:44 +02:00
DJj123dj ce6ca6fc09 Update livestatus.json 2025-06-24 10:42:04 +02:00
DJj123dj e3bc985c0c Updated all versions to v4.0.7 2025-06-24 10:38:33 +02:00
DJj123dj ff8568fde7 Added the 8 new languages to the system + readme 2025-06-24 09:05:13 +02:00
DJj123dj 8ec710fa55 Renamed simplified-chinese translation file 2025-06-24 08:49:39 +02:00
Jasper 076fcbcb37 Added 8 new languages by HanumeshGupta (used AI)
Added Language and update Readme
2025-06-24 08:46:56 +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
Hanumesh Gupta 8dfd17ddde Added Tamil and updated readme 2025-06-19 22:01:14 +05:30
Hanumesh Gupta 5f077e87f6 Added more Language and edited readme 2025-06-19 15:48:18 +05:30
Hanumesh Gupta 76dc3b1022 Added More Language and Updated Readme 2025-06-19 15:20:22 +05:30
Hanumesh Gupta 93ed56b4ae Added Language and update Readme 2025-06-19 12:46:03 +05:30
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
Jasper a6be9df213 Fixed validation in UniqueIdArray checker (by guillee3)
Fixed validation in UniqueIdArray checker
2025-06-01 17:58:38 +02:00
guillee3 6b2f291122 Fixed validation in ODCheckerCustomStructure_UniqueIdArray to reject string inputs. 2025-06-01 15:02:46 +02:00
DJj123dj af0dc879c6 Merge branch 'dev' into dev-v4.1.0 2025-05-31 11:07:36 +02:00
Jasper a12f60136a Open Ticket v4.0.6
Open Ticket v4.0.6
2025-05-31 11:01:13 +02:00
DJj123dj 1d05d04bf2 Updated all versions to v4.0.6 2025-05-31 10:51:29 +02:00
DJj123dj ff6a9eefaf Update package.json 2025-05-31 10:43:04 +02:00
DJj123dj aa9004d7e2 Compare/diff persian language + translation improv 2025-05-31 10:42:18 +02:00
Jasper 7e1fe78a26 Merge pull request #172 from zhavis/development (Persian translation)
Create persian.json
2025-05-31 10:29:05 +02:00
DJj123dj 675571b8d8 Added recommended hostings to README.md
Want to advertise your hosting too? Contact us in our discord server.
2025-05-31 10:24:32 +02:00
DJj123dj 48f0d65fad Fixed slash cmd registration bug
This was caused by already registered commands of a different type than 'chatInput'.
2025-05-31 10:24:04 +02:00
zhavihosseini a54422b222 Create persian.json 2025-05-28 07:11:03 +03:30
DJj123dj c243d03216 Merge branch 'dev' into dev-v4.1.0 2025-05-03 21:15:03 +02:00
Jasper b965a4f5a2 Open Ticket v4.0.5
Open Ticket v4.0.5
2025-05-03 21:09:55 +02:00
DJj123dj 0d8dd8cd17 Updated all versions to v4.0.5 2025-05-03 21:02:47 +02:00
DJj123dj 6ac432cadc Fix: Panels now require at least 1 option. 2025-05-03 20:59:22 +02:00
DJj123dj dec6c7598b Improv: Decreased QuickBuilder boilerplate params 2025-05-03 20:58:50 +02:00
DJj123dj 2de2e3cd9b Fixed option suffixes with ot-sqlite-database 2025-05-03 18:34:10 +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 5d0776a9b9 Open Ticket v4.0.4
Open Ticket v4.0.4
2025-05-01 14:10:55 +02:00
Jasper 7bf843c851 Dev v4.0.4
Dev v4.0.4
2025-05-01 14:05:56 +02:00
DJj123dj 3e3ea9263f Updated all versions to v4.0.4 2025-05-01 14:05:06 +02:00
DJj123dj bb5c3e9c8d Support for HTML Transcripts v2.1 2025-05-01 13:56:38 +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
Jasper 94f32674cb Open Ticket v4.0.3
Open Ticket v4.0.3
2025-04-28 18:27:48 +02:00
DJj123dj e8eb52e00b #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:22:54 +02:00
DJj123dj 4efa165949 Updated all versions to v4.0.3 2025-04-27 18:21:56 +02:00
DJj123dj 4c3e4ef9a5 Fixed bug due to discord.js new components 2025-04-27 18:17:38 +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
Jasper 6355fb5562 Open Ticket v4.0.2
Open Ticket v4.0.2
2025-04-12 19:19:06 +02:00
DJj123dj 442c201994 Updated all version badges to v4.0.2 2025-04-12 19:02:57 +02:00
DJj123dj b61f9d4d5a Update README.md 2025-04-12 18:57:53 +02:00
DJj123dj 129053b9f5 Fixed autoclose hours showing the wrong number.
Fixed that the ticket message embed shows the wrong amount of hours when used manually with /autoclose enable
2025-04-08 22:06:22 +02:00
DJj123dj 35a0511b97 Merge branch 'dev' of https://github.com/open-discord-bots/open-ticket into dev 2025-04-08 22:01:38 +02:00
DJj123dj bae221056c Fixed #149 Tickets not changing categories 2025-04-08 22:01:36 +02:00
DJj123dj 9c6e4ccde4 Update checkerLoader.ts 2025-04-08 21:59:08 +02:00
Roppl3r ac6845b4d4 Fixed Issue #151 (Max length for option 'name' should be 45, currently set to 50)
Fixed config checker bug related to the name of a ticket option in a dropdown.
2025-04-08 21:58:03 +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 75770c84b2 Update README.md preview images 2025-04-08 09:03:41 +02:00
DJj123dj 2281d78896 Implemented Vietnamese translations 2025-04-07 18:13:23 +02:00
ngocdiep2006 37cc3b794e Added Vietnamese translation
Create vietnamese.json
2025-04-07 18:07: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
Jasper 9414b96221 Open Ticket v4.0.1
Open Ticket v4.0.1
2025-04-02 14:49:08 +02:00
Jasper 0befd2860e Open Ticket v4.0.1
Open Ticket v4.0.1
2025-04-02 14:35:53 +02:00
DJj123dj 74d4ac24f2 Added new plugins to README.md 2025-04-02 07:34:42 +02:00
DJj123dj 364bf4f702 #144 : Added quick builders for plugins 2025-04-01 20:12:42 +02:00
DJj123dj dd5b76f3d8 Update README.md 2025-03-31 21:36:07 +02:00
DJj123dj a02ba56e9a Updated README.md 2025-03-31 17:54:10 +02:00
DJj123dj 6d5bdc5dd4 #143 : Panels now edit after using dropdown
From now on, all panels will update/edit themselfs after a dropdown has been used. This will reset the dropdown selection in the user client which results in a smoother user experience.
2025-03-29 13:16:27 +01:00
DJj123dj 51bfb1a367 #126 : ODManagers now use Maps instead of Arrays
This results in a minor speed increase within large managers (e.g. builders, tickets, blacklist, ...)
2025-03-29 13:14:59 +01:00
DJj123dj d85d842046 #142 : ODVersion + ODHTTPRequest improvements
Added new methods to ODVersion class & added User-Agent header to ODHTTPRequest classes
2025-03-29 13:13:35 +01:00
DJj123dj 53136e0e6c Fixed .DS_Store bug in plugin launcher 2025-03-29 13:10:58 +01:00
DJj123dj 2031a82523 #141 : Finished type simplifications 2025-03-29 13:10:27 +01:00
DJj123dj 453c48fab5 Update init.ts 2025-03-28 17:36:50 +01:00
DJj123dj fd3bc0e7b9 API: allowCategoryChange param in related actions
Added the 'allowCategoryChange' parameter in related ticket actions (claim, unclaim, close, reopen) to disable the changing of the category. This can be used by plugins not wanting the category of the channel to change.
2025-03-27 16:30:37 +01:00
DJj123dj f316ae83e7 Update CONTENT_CREATORS.md 2025-03-26 15:29:31 +01:00
DJj123dj d2c5cf0fc1 Added extra comments in some places 2025-03-26 15:24:56 +01:00
DJj123dj 45dfc18c1d Updated all file versions to v4.0.1 2025-03-26 15:14:11 +01:00
DJj123dj d81b1e7be3 The 4 new languages can now be used in OTv4.0.1 2025-03-26 15:13:55 +01:00
Jasper 2a82bdebae Added 4 new languages (by NoOneNook)
Added 4 new languages + updated README.md
2025-03-26 14:54:39 +01:00
NoOneNook c9e13b2471 Update README.md 2025-03-25 23:58:45 +01:00
NoOneNook cbeac2fda2 Update README.md 2025-03-25 23:50:12 +01:00
NoOneNook 8b1dd7d27a Update README.md 2025-03-25 23:47:34 +01:00
NoOneNook 9e7cd28c6a Russian language file
I created a Russian language file using AI. This file has been verified in several other places and was also reviewed using ChatGPT afterwards to ensure that everything is correct, properly structured, and contains the necessary translations and messages for the system.

ChatGPT was not used for translation, only to check that the entire file is correctly written in the Russian language
2025-03-25 23:46:52 +01:00
NoOneNook fabe11bfd1 Update README.md 2025-03-25 23:22:26 +01:00
NoOneNook 79187b9995 Add files via upload 2025-03-25 23:21:29 +01:00
NoOneNook a1251cf11c Delete languages/latvian.json
Wrong file!
This language will be translated soon
2025-03-25 22:46:28 +01:00
NoOneNook 1bc8b2b401 Latvian (coming soon)
tarted in a new language. Soon to be finished.
2025-03-25 22:43:51 +01:00
NoOneNook ce727e52b0 Update on swedish.json
Updated 
"translators": ["NoOneNook / ItsNook_"],
to 
"translators": ["NoOneNook"],
2025-03-25 22:13:24 +01:00
NoOneNook 30233df7b0 Update on norwegian.json
Updated "translators": ["NoOneNook"], to "translators": ["NoOneNook"],
2025-03-25 22:10:54 +01:00
NoOneNook 48d3296868 Delete languages/README.md
Deleted README.md from wrong folder
2025-03-25 22:01:09 +01:00
NoOneNook 6b8ef26b4c Remove README.md
Deleted README.md text from wrong folder
2025-03-25 22:00:25 +01:00
NoOneNook 9c992c3dbd name fix
Updated readme below the lines for Norwegian and Swedish
2025-03-25 21:58:19 +01:00
NoOneNook 6feb56c25c Name fix
Fixed lines for Norwegian and Swedish
2025-03-25 21:56:40 +01:00
NoOneNook e29b6afd87 Translate update
Updated readme below the lines for Norwegian and Swedish
2025-03-25 21:54:18 +01:00
NoOneNook d35b7fca99 Add files via upload
Norwegian and Swedish translation complete
2025-03-25 21:49:40 +01:00
DJj123dj 126fada990 Merge branch 'dev' 2025-03-25 18:44:56 +01:00
DJj123dj bd4929f11b Updated all URLs for repository transfer 2025-03-25 17:49:42 +01:00
DJj123dj d28fcf40d7 Fixed broken flag alias using --dev-database 2025-03-22 11:08:09 +01:00
DJj123dj bed4e2a56a Improved CONTRIBUTING.md & SECURITY.md markdown 2025-03-16 18:35:25 +01:00
DJj123dj ba3a35a343 Added official Open Ticket Pterodactyl eggs! 2025-03-16 18:34:43 +01:00
DJj123dj 1e159c9bab Improved dockerfile & compose 2025-03-12 22:13:00 +01:00
DJj123dj e7ad3dd50b Fixed startup crash codes (for Docker) 2025-03-12 22:12:51 +01:00
DJj123dj 177dee7468 Type simplifications (for documentation) 2025-02-27 20:51:59 +01:00
DJj123dj 0d40c23aa8 Fixed HTML Transcripts "member not found" error. 2025-02-19 15:48:37 +01:00
DJj123dj 15eef95fdd Decreased /clear speed to reduce ratelimit chances 2025-02-19 15:43:43 +01:00
DJj123dj c89e93d33d Fixed crash resulting of non-returning statements 2025-02-19 15:29:16 +01:00
DJj123dj e6c413cdf5 Fixed typos in comments 2025-02-18 18:18:10 +01:00
DJj123dj b12556120c Made database migration compatible with plugins 2025-02-17 19:44:18 +01:00
DJj123dj 58674452d8 Added quick setup to README.md 2025-02-15 19:01:53 +01:00
DJj123dj a2f9593d55 Fixed #134 : text transcripts incorrect newlines 2025-02-15 18:22:23 +01:00
DJj123dj 9702c4d97d Fixed stats resetting due to breaking change 2025-02-12 16:27:34 +01:00
DJj123dj f13777e5f3 Update README.md 2025-02-09 11:34:37 +01:00
DJj123dj 9395e9af75 Added 13 new utility events for plugins 2025-02-09 11:19:03 +01:00
DJj123dj b2a5e65fe2 Update README.md 2025-02-09 11:18:24 +01:00
DJj123dj 071c8f953c Update createDocs.js 2025-02-05 17:04:10 +01:00
DJj123dj 9a4e5e2175 Merge branch 'dev' of https://github.com/DJj123dj/open-ticket into dev 2025-02-04 17:42:37 +01:00
DJj123dj 4e709f2a0b Update createDocs.js 2025-02-04 17:42:24 +01:00
Jasper 27e3e78439 Fixed dateString() utility
Fixed dateString() utility
2025-02-03 21:28:51 +01:00
DJj123dj 06e7919162 Fixed panel dropdown bug with empty option label 2025-02-03 21:01:36 +01:00
Roppl3r bf952e3ef7 Fixed month parsing in dateString() utility causing incorrect dates in text transcripts. 2025-02-02 20:23:32 +01:00
DJj123dj a397b66c9e Update createDocs.js 2025-02-01 11:52:02 +01:00
DJj123dj 27f6e3a81f Compiler will now only hash JS & TS files 2025-02-01 11:23:17 +01:00
DJj123dj f2cefae37c database will auto-update for the breaking change 2025-02-01 11:22:49 +01:00
DJj123dj 6acbf74846 BREAKING: changed all ids to "opendiscord:" 2025-02-01 10:29:35 +01:00
DJj123dj 29f8e1452c BREAKING: changed openticket to opendiscord in API 2025-02-01 10:22:27 +01:00
DJj123dj ce242741cd Update README.md 2025-02-01 10:05:26 +01:00
DJj123dj ac587568d3 Update createDocs.js 2025-01-31 17:14:56 +01:00
DJj123dj f5e78949d7 Added typedoc for OT Docs API Reference 2025-01-31 16:03:02 +01:00
DJj123dj 8e7fe4e502 Update README.md 2025-01-29 21:36:26 +01:00
DJj123dj e51cc54090 Fixed polish translation 2025-01-29 19:50:31 +01:00
Jasper dc3515da10 Added Polish Translation
Added Polish Translation
2025-01-29 19:47:25 +01:00
Jasper c57f3397a4 Update checker.ts 2025-01-29 19:46:04 +01:00
Jasper 686b45b0bd Added Polish Translation
Added Polish Language 🇵🇱
2025-01-29 19:43:53 +01:00
DJj123dj 96d6758b4a Update README.md 2025-01-29 19:35:10 +01:00
DanoGlez 52b49ec564 Added Custom Emojis
feat: allow custom emojis by modifying checker
2025-01-28 23:23:18 +00:00
DanoGlez deebd2b758 Added Polish 2025-01-28 23:14:55 +00:00
DJj123dj ed9afffd70 Finished API comments 2025-01-28 18:16:14 +01:00
Jasper 91cad963e5 Added new API comments (part 9-12)
Added new API comments (part 9-12)
2025-01-28 17:50:23 +01:00
JasperAtSchool baa4e35878 Added new API comments (part 12) 2025-01-28 14:33:10 +01:00
JasperAtSchool f6c70fa62a Added new API comments (part 11) 2025-01-28 14:22:26 +01:00
JasperAtSchool 58fb7b7512 Added new API comments (part 10) 2025-01-28 14:16:43 +01:00
JasperAtSchool 5e1cfe7942 Added new API comments (part 9) 2025-01-28 11:21:49 +01:00
DJj123dj 507d254556 Added new API comments (part 8) 2025-01-27 23:37:49 +01:00
DJj123dj 43356fef05 Added Lithuanian translation (by tsgindrius) 2025-01-27 23:15:58 +01:00
DJj123dj bbae139c6b Updated README.md 2025-01-25 23:10:45 +01:00
DJj123dj 6516857866 Fixed deprecation of HTML Transcripts interactions 2025-01-25 22:54:50 +01:00
DJj123dj 213ec32225 Fixed small bugs 2025-01-25 14:06:41 +01:00
DJj123dj 04789d5426 Fixed #121 : Ticket busy will now reset on reboot 2025-01-25 11:47:50 +01:00
DJj123dj 0437af1507 Fixed #120 : Temporarily removed bot status state
Will be added back in a future update, but customisable instead of double text :)
2025-01-25 11:43:13 +01:00
DJj123dj 73d188c5dc Fixed #129 : Improved invalid token error 2025-01-25 11:28:32 +01:00
DJj123dj 057a5fc6ae Fixed #124 : DMs are now sent to ticket creator 2025-01-25 11:18:10 +01:00
DJj123dj d35eb8f8bf Updated issue templates 2025-01-25 10:56:18 +01:00
DJj123dj 19e5902320 Fixed: checker not working correctly using .env
Fixed the config checker not working correctly when parsing the token from the .env file.
2025-01-24 17:34:05 +01:00
DJj123dj 66b0377a2b Update index.ts 2025-01-22 17:11:01 +01:00
DJj123dj 3bd308007c Updated sponsors 2025-01-22 16:58:24 +01:00
DJj123dj 40bd873a33 Added new API comments (part 7) 2025-01-22 15:13:13 +01:00
Jasper 09d9b2d265 Added new API comments (part 6)
Added new API comments (part 6)
2025-01-21 18:51:06 +01:00
JasperAtSchool 7fb169789b Added new API comments (part 6) 2025-01-21 12:05:36 +01:00
DJj123dj 1ab207b4eb Found more missing translations 2025-01-20 20:42:23 +01:00
DJj123dj 5e5ac3e409 Implemented progress bars when loading slash cmds 2025-01-19 15:16:04 +01:00
DJj123dj 7809ffc5c6 Fixed typo's in comments 2025-01-19 15:15:29 +01:00
DJj123dj 9da823aa4b Added new progress bars API & classes 2025-01-19 00:17:39 +01:00
DJj123dj ff23b0e89e Added comment to dump command 2025-01-18 21:53:53 +01:00
DJj123dj c3474fb017 Made built-in cmds compatible with slash update 2025-01-18 20:41:12 +01:00
DJj123dj 03982f644e MAJOR: Full rewrite of slash cmd loading system
The system of detecting, comparing & registering slash cmds has been rewritten and updated for future compatibility. This will break all current plugins (so please wait until they are updated)

=> Increased speed
=> All cmd params are now used when comparing
=> Future compability & removed deprecated functions
=> New universal utility interfaces for slash commands
=> And more!
2025-01-18 20:40:52 +01:00
DJj123dj 3a30152cb2 Improved startup time (5 sec) due to config bug 2025-01-18 20:36:08 +01:00
DJj123dj 334c9dde21 Moved sponsors more to top of README.md 2025-01-14 18:14:23 +01:00
DJj123dj b6985f9d28 Updated LICENSE 2025-01-14 17:56:27 +01:00
DJj123dj 7d30bf24d9 The bot will now only re-compile when changed
The bot will now only re-compile when the plugins or source code changes. (computed using a hash)

This reduces the average startup time by 50%.
2025-01-14 17:53:11 +01:00
DJj123dj 138378ac31 Added new API comments (part 5) 2025-01-14 17:18:47 +01:00
DJj123dj 9a73ee0d01 Fixed bug with option description being empty
Fixed a bug which caused the /panel command to crash when using an option without any description in combination with the "describeOptionsInEmbedFields" mode enabled.
2025-01-14 16:46:22 +01:00
Jasper f26e6f5e99 Added new API comments (part 4)
Added new API comments (part 4)
2025-01-14 16:40:17 +01:00
JasperAtSchool 209041c1e5 Merge branch 'DJj123dj:dev' into dev 2025-01-14 16:38:39 +01:00
JasperAtSchool 499901fbf1 Added new API comments (part 4) 2025-01-14 16:37:43 +01:00
DJj123dj 1a1a3f3b02 Update README.md 2025-01-11 22:27:52 +01:00
DJj123dj dd5374f84c Update plugin.json 2025-01-10 23:01:03 +01:00
DJj123dj 02e8b7d0a1 Added new API comments (part 3) 2025-01-09 19:42:11 +01:00
DJj123dj ce0af7e1e9 Added new API comments (part 2) 2025-01-09 18:34:29 +01:00
Jasper e1fab310a4 Added new API comments (part 1)
Added new API comments (part 1)
2025-01-09 18:03:28 +01:00
JasperAtSchool 0615235780 Added new API comments (part 1) 2025-01-09 17:48:50 +01:00
DJj123dj abd672dd3b Small bug fixes (plugin system) 2025-01-07 19:36:29 +01:00
Jasper f05d2d7d1b Update README.md
Updated v4.0 beta announcement
2025-01-07 18:54:24 +01:00
DJj123dj e65a092bc0 Small bug fixes 2025-01-06 22:29:09 +01:00
DJj123dj 17e1902681 Updated discord.js to v14.17.2 2025-01-06 21:14:41 +01:00
DJj123dj 91657f7519 Fixed ot-sqlite-database plugin stats not working 2025-01-06 20:51:36 +01:00
DJj123dj cfa692b356 Updated README.md year to 2025 😄 2025-01-06 20:51:11 +01:00
Jasper 0604e334ed Open Ticket v3.5.9
**Improved:**
- Updated discord.js to `v14.17.2`

**Fixed:**
- discord.js PermissionOverwrites => `Supplied parameter is not a User nor a Role`
- message `"ephemeral"` is deprecated warning
- A few config checker typos
2025-01-04 09:52:57 +01:00
DJj123dj e9d20c0a14 Improvements + Updated discord.js to v14.17.2 2025-01-04 09:50:17 +01:00
DJj123dj 67465bfdf4 Bugfix: ephemeral in message reply is deprecated 2025-01-04 09:49:52 +01:00
DJj123dj a66d3aad2d Updated to v3.5.9 2025-01-04 09:36:42 +01:00
DJj123dj f2de7fc408 Bugfix: discord.js PermissionOverwrites id invalid 2025-01-04 09:35:48 +01:00
DJj123dj 0cbf991090 Update transcripts.json 2024-12-25 21:05:01 +01:00
DJj123dj 72f7cbce26 Improved default config message descriptions. 2024-12-25 18:21:37 +01:00
DJj123dj 05b4080ac5 Plugins can now use import path shortcuts
Plugins are now able to use import path shortcuts to import all API classes, utilities and the openticket globals. You can now import everything from "#opendiscord" instead of "../../src/index"
2024-12-25 18:20:58 +01:00
DJj123dj 7646b3fc35 Increased HTML Transcript log level 2024-12-15 20:11:39 +01:00
DJj123dj bf13e15d56 Disabled autoclose & delete by default 2024-12-15 19:47:20 +01:00
DJj123dj f0183c319a Added Hindi Translation (by an_developer) 2024-12-15 19:46:40 +01:00
DJj123dj c6e836455c Updated README.md Contributors 2024-12-15 19:46:10 +01:00
DJj123dj 8a6a8e527f Added 6 new plugins to README.md 2024-12-08 18:29:00 +01:00
DJj123dj 4e7d45ea07 Increased button label length to 80 characters 2024-12-08 18:28:49 +01:00
DJj123dj b7f5c7191d Fixed responder TS typo 2024-12-08 18:28:22 +01:00
DJj123dj 106cb0f718 Fixed dropdown not supporting empty emojis
Fixed a bug that caused the bot to crash when spawning a dropdown panel which includes an option without emoji.
2024-12-07 21:46:49 +01:00
DJj123dj 0c2b688862 Fixed English translation typo's 2024-12-07 13:27:24 +01:00
DJj123dj a037d76060 Fixed database ghosted unused-options bug
Fixed a problem where unused ticket options would stay in the options database in certain circumstances. Even when there is no ticket that uses the option anymore.
2024-12-07 00:34:49 +01:00
DJj123dj 1e2321313f Implemented "removeParticipantsOnClose"
Implemented the option "removeParticipantsOnClose" in the config/general.json. It wasn't implemented in the code by accident.
2024-12-06 15:59:08 +01:00
DJj123dj 91ea12a6cc Added Arabic translation 2024-12-06 15:44:58 +01:00
DJj123dj 5d86269743 Fixed stats not displaying with SQLite plugin
Lot's of stats were hidden in the embed when using the ot-sqlite-database plugin. This was caused by a recent API breaking-change that made everything async.

The problem wasn't related to the plugin itself, but the Open Ticket api. Updating the plugin is not required.
2024-12-05 22:10:02 +01:00
DJj123dj 42ff447a94 Added French & Turkish translations 2024-12-05 21:47:17 +01:00
Jasper 7627c07b71 Added French Translation (+ Catalan fixes)
Translations Updates
2024-12-05 21:42:33 +01:00
Guillem Parera Jové 147ebc90da - Fixed Catalan Translation.
- Added French Translation.
2024-12-05 21:03:14 +01:00
DJj123dj e8319cccea Updated README.md 2024-11-25 20:06:11 +01:00
DJj123dj 66ab30bf06 Livestatus source moved to github 2024-11-25 19:46:56 +01:00
DJj123dj d0ef2a9873 Language loading => async + moved livestatus file 2024-11-25 19:43:58 +01:00
DJj123dj 3e928ed06e Bug fixes for new database system 2024-11-25 19:24:48 +01:00
DJj123dj b22f7b2de7 major bug fixes & breaking change in database code
Major bug fixes and improvements have been made. There's also a breaking change for plugins using the database system. All database related functions are now async based.
2024-11-24 23:26:14 +01:00
DJj123dj 6b36b1caf6 Update plugin.json 2024-11-21 23:43:39 +01:00
DJj123dj f8da68607d Update client.ts 2024-11-18 20:50:02 +01:00
DJj123dj a31588f025 Small changes to the plugin loading system 2024-11-18 20:19:25 +01:00
DJj123dj 5a4b45aa3f Added Thai Translation (by ModsHD) 2024-11-14 17:50:06 +01:00
DJj123dj 92aa42fc7c ticket creator bug fix + added danish translation 2024-11-11 21:27:53 +01:00
DJj123dj 4022ae49c2 Major bug fixes + updated contributing guidelines 2024-11-06 20:59:00 +01:00
DJj123dj 319bbe318d Added previews + plugins to README.md 2024-10-28 13:52:14 +01:00
DJj123dj 970807d144 Added 2 new languages + small ODMain improvements 2024-10-28 12:29:33 +01:00
Jasper 929f57721b Added Estonian and Finnish translations (by iamnotmega) 2024-10-28 12:23:05 +01:00
NotMega 3469e61065 Update finnish.json 2024-10-26 19:45:34 +03:00
NotMega 3ddb03a569 Update estonian.json 2024-10-26 19:45:17 +03:00
NotMega 3fa72e07d8 Create finnish.json 2024-10-26 19:42:01 +03:00
NotMega 6423ed4b41 Create estonian.json 2024-10-26 19:41:36 +03:00
Jasper fc8a252e67 Translation Credits
Gives some credit to AI that notmega used while doing his translations
2024-10-21 18:29:12 +02:00
Ronalds ca9fe0aa77 add some credit to ai 2024-10-20 18:16:26 +03:00
DJj123dj 798ab8fab1 Update README.md 2024-10-16 14:22:17 +02:00
DJj123dj 848c968483 Fixed all currently known bugs :) 2024-10-14 20:30:45 +02:00
DJj123dj 330ea47ab9 Updated german & fixed async forEach errors 2024-10-07 22:24:02 +02:00
DJj123dj 4e10cba203 Added docker compatibility 2024-10-06 21:17:32 +02:00
DJj123dj 56e1fb3939 Fixed loopAll() async errors 2024-10-03 20:46:01 +02:00
DJj123dj 7c76be8473 Fixed suffix register error on config reload 2024-10-03 20:16:42 +02:00
DJj123dj 7fa5dca7b3 Updated Italian 2024-10-02 23:28:00 +02:00
DJj123dj 6d3f210612 Added Italian by fraden1mvp 2024-10-02 23:21:20 +02:00
DJj123dj 118161698e Fixed blacklist bug 2024-10-02 22:41:12 +02:00
DJj123dj 6a8a8dd34d languages/config improvements & bug fixes 2024-10-02 17:36:08 +02:00
DJj123dj 28f0d974e5 Fixes "successfully" typo. 2024-09-24 19:32:35 +02:00
DJj123dj 77c486e2b7 Update createTranscript.ts 2024-09-15 17:55:42 +02:00
DJj123dj 20bf25aa98 Small bug fixes + auto cycles are now debug only! 2024-09-12 21:14:26 +02:00
DJj123dj 70048fa399 Added in-code flags support for pterodactyl 2024-09-11 21:08:05 +02:00
DJj123dj 8eae88b1c6 Added 2 new plugins to README.md 2024-09-05 23:27:40 +02:00
DJj123dj 35b02e0a8f Added first plugins 2024-09-05 21:01:36 +02:00
DJj123dj 2a8e41005f Update init.ts 2024-09-05 20:58:57 +02:00
DJj123dj 85026793a4 Added Indonesian by erxg 2024-09-04 20:39:20 +02:00
DJj123dj 55bcff8030 fix ticket cmd perms, updated djs & added dump cmd 2024-09-04 20:13:13 +02:00
DJj123dj 39099a0dfe Fixed small translation mistakes 2024-09-02 16:10:02 +02:00
DJj123dj 504889a83d Fixed config checker emoji count 2024-09-01 21:30:56 +02:00
DJj123dj c1c80fc04d Small improvements 2024-09-01 20:55:50 +02:00
DJj123dj 4e030bf95b Fixed config checker bugs & added new flags 2024-09-01 18:38:08 +02:00
DJj123dj adae64bab9 Romanian & Ukrainian languages are now available! 2024-09-01 17:53:29 +02:00
DJj123dj bfa05297b2 Fixed plugins not loading on windows 2024-09-01 17:30:25 +02:00
DJj123dj 509e961446 Typescript will now be compiled without CLI 2024-09-01 16:27:25 +02:00
DJj123dj 147eae05b3 Testing pterodactyl compile support! 2024-09-01 11:58:16 +02:00
DJj123dj e410ceaa64 Added Ukrainian & Romanian languages 2024-08-31 21:55:52 +02:00
Jasper 51e1ca6fa3 Added Ukrainian by Anderskiy
upload a new language
2024-08-31 21:44:02 +02:00
Anderskiy e67c505e32 Add files via upload 2024-08-31 15:08:14 +03:00
DJj123dj 12885f63d6 Fixed spanish max string length issues 2024-08-23 23:24:59 +02:00
Jasper c49f577f7f Added Spanish by Josuens & Reddishye
Added Spanish Language
2024-08-23 23:21:09 +02:00
DJj123dj d5295a9480 Fixed bugs & added/updated languages! 2024-08-23 23:19:44 +02:00
DJj123dj 177ab95dba Fixed permission error & preparing for win build 2024-08-22 21:22:19 +02:00
DJj123dj 5e2e0417dc Update index.js 2024-08-22 16:17:22 +02:00
DJj123dj b3cd37bb4b Update index.js 2024-08-22 16:13:27 +02:00
DJj123dj 5512bc1134 Pterodactyl panel support part 2 2024-08-22 16:09:29 +02:00
DJj123dj 7dd9bf1ac0 OTv4 is now pterodactyl panel compatible! 2024-08-22 15:45:38 +02:00
Hugo Torres cd7fb7533f Add Spanish Language 2024-08-22 11:39:07 +02:00
DJj123dj 8e730fdfae Tried to fix typescript not working on pterodactyl 2024-08-22 10:28:24 +02:00
DJj123dj 1dd8c0246a Fixed some translation errors & added Catalan! 2024-08-21 22:46:54 +02:00
DJj123dj 0a69efdfe5 Added german translation by benzorich 2024-08-21 14:52:33 +02:00
Jasper 9028eaa16a Update README.md 2024-08-21 14:24:55 +02:00
DJj123dj 0c2aca7039 Open Ticket v4 Beta 2024-08-21 13:24:56 +02:00
DJj123dj 9326d0fae8 v4.0.0 preparations 2024-08-21 11:56:47 +02:00
Jasper e1f4936ae6 Open Ticket v3.5.8
**fixed:**
- updated hungarian translation (by kornel0706)
- updated turkish translation (by 0x15d3)
- updated portuguese translation (by quiradon)
- updated czech translation (by spyeye_)
- fixed "unknown interaction" on ticket creation (in certain situations)
2024-08-02 14:51:17 +02:00
DJj123dj fd3ae39150 v3.5.8 2024-08-02 14:46:53 +02:00
Jasper 1b0da061ba Update czech.json 2024-07-31 11:52:35 +02:00
Jasper 07eb8f34e9 Update turkish by 0x15d3
Update turkish.json
2024-07-31 11:44:27 +02:00
artren 64fef62bf5 Update turkish.json 2024-07-27 23:18:34 +03:00
Jasper b2be76f5b6 Update Portuguese by Quiradon
Update portuguese.json
2024-07-23 20:22:29 +02:00
QuiraDon 856ccc10b9 Update portuguese.json 2024-07-23 14:27:08 -03:00
318 changed files with 52651 additions and 12769 deletions
+13
View File
@@ -0,0 +1,13 @@
# Exclude local build artifacts — these are regenerated inside the container
node_modules/
dist/
.git/
.backup/
.vscode/
devconfig/
devdatabase/
build-docker
otdebug.txt
plugins/*
!plugins/example-plugin/
!plugins/example-plugin/**
+39
View File
@@ -0,0 +1,39 @@
# 🎥 Content Creators
Hi there! We're searching for content creators to create tutorials & setup guides for Open Ticket.
You can get many great rewards from helping us!
You can choose which type of tutorial you want to make. You can create a quick setup, full setup, plugin tutorial or something else!
You are not required to talk in the video, but make sure it has some kind of subtitles instead.
### 📄 Requirements
- 📌 Video length of at least 2 minutes.
- 📌 Provide a link to the Open Ticket repository in the description of the video.
- 📌 Please use the latest version of Open Ticket available at the time of making the video.
- 📌 **(Optional)** Recommended language is English.
- 📌 **(Optional)** Recommended platform is YouTube!
- 📌 **(Optional)** Speech in the video *(AI is allowed)*
### 🎁 Rewards
We won't give out any compensation in money, but we will give you some advantages!
- ✅ Advertisement of tutorial in the [README.md](../README.md)!
- ✅ Advertisement of channel in our [discord server](https://discord.dj-dj.be)!
- ✅ `Content Creator` role in our [discord server](https://discord.dj-dj.be)!
- ✅ Advertisement of tutorial & channel in documentation!
- ✅ Lot's of of views generated from GitHub traffic!
- ✅ A completely free 24/7 hosted Open Ticket bot!
- ✅ We will advertise the video to all our socials!
### 💤 Outdated Content
When a tutorial or setup guide is outdated or no longer applicable,
we will remove it from the `README.md` & documentation.
Your channel will still stay in the list of contributors, so don't worry about that!
You will be noticed at least a month before removal.
This way you can take time to create an updated tutorial if you want.
### 💬 Contact Us
If you've read all these guidelines, requirements & rewards,
you can create a ticket in our [discord server](https://discord.dj-dj.be) to start working on it!
We will try to help you as fast as possible!
+84 -36
View File
@@ -1,44 +1,92 @@
# Contributing
## Translation
### Step 1: checking
1. [Check if the language isn't already in open ticket!](/language/)
2. Check if the current translation is up-to-date!
3. Can you edit/write JSON?
4. [Have you read the documentation?](https://otdocs.dj-dj.be)
# Contributing Guidelines
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="500px">
If there is translation missing, you go to the next step!
[![discord](https://img.shields.io/badge/discord-support%20server-5865F2.svg?style=flat-square&logo=discord)](https://discord.com/invite/26vT9wt3n3) [![version](https://img.shields.io/badge/version-4.2.0-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0) [![Sponsor DJj123dj](https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors)](https://github.com/sponsors/DJj123dj)
### Step 2: translating
**RULES:**
- Always upload or create a pull request to the [__`dev` branch!__](https://github.com/DJj123dj/open-ticket/tree/dev)
- Don't change anything else, except the translation you are adding!
- Translations are checked before uploading, so don't write things that aren't specified in the json file
- Always start translating from `english.json` or `custom.json`! Never from another language.
These are the Contributing Guidelines of Open Ticket!<br>
Here you can find everything you need to know about contributing to Open Ticket.<br>
This includes new features, translations & bug fixes.
If you translate, you need to change 2 files! The first one is required, the other 2 are optional & will be added by us if you don't do it!
1. add `yourlanguage.json` to the `/language/` directory
2. add your username to the `_CREDITS` part of `yourlanguage.json`!
3. add your discord username in the translator table in `README.md`
### 💬 Translations
#### Step 1: View & Check
1. Check the [`./languages/`](./languages/) folder for existing translations.
2. When it doesn't exist yet, visit **[step 2](#step-2-translation)**.
3. When it does exist, check if it is outdated, incorrect or incomplete.
4. If you match one of these requirements, you can also visit **[step 2](#step-2-translating)**.
### Step 3: waiting
Translations will always be added in the **next version** of Open Ticket! They will never be added to the `main` branch directly without anything else! This means that it can take some time before your translation is in the official version!
#### Step 2: Translation
1. If your language doesn't exist yet, copy the [`english.json`](./../languages/english.json) file and rename it to your language.
2. **(❌ Not Recommended)** You are allowed to use `Google Translate` or `DeepL`, but only when you say it in the pull request and enable the `"automated"` boolean.
3. Only translate existing values in the json file. Don't add or remove any values from the file.
4. You're **NOT REQUIRED** to translate everything! If you only want to translate a part of it, it's okay!
5. If you are unable to translate something, leave it in `English`.
## Writing plugins
We are going to create an official repository for plugins soon!
But don't worry! You can already write some plugins!
#### Step 3: Metadata
Metadata can be found in the `_TRANSLATION` variable.
|Value |Notes |
|--------------|-------------------------------------------------------------------------------------------------|
|`otversion` |The Open Ticket version of this translation. **❌ DON'T EDIT!** |
|`translators` |The discord usernames from the translators. Add your name to the list when you've contributed. |
|`lastedited` |The last edited date in the `DD/MM/YYYY` format. |
|`language` |The full name of the language with capital letter. |
|`automated` |Enable this boolean when this translation has been made using ChatGPT, Google Translate, ... |
**RULES:**
- Always specify the versions that the plugin is compatible with!
- If extra npm dependencies are needed, then they also need to be specified!
- If there are errors while you write a plugin/edit the code in some way. Then we can't help you with fixing the bug. We only fix bugs that are part of the original unedited version of Open Ticket!
> #### ✅ You are also allowed to add your username to the [`README.md`](./../README.md) translator list!
## Other versions of contributing
If you want to contribute in another way, feel free to join [our discord server](https://discord.dj-dj.be) & talk about how you are going to do it. We will always help you further!
#### Step 4: Uploading
1. When adding a new language, please mention @DJj123dj. He will add the code required for the language to work.
2. There are currently 3 ways of uploading translations:
- Create a pull request to the [__`dev` branch!__](https://github.com/open-discord-bots/open-ticket/tree/dev)
- Create a ticket in our [discord server](https://discord.dj-dj.be)!
- Send a DM to @DJj123dj on discord!
3. Now you'll need to wait until the next Open Ticket version for it to be added.
<br>
CONTRIBUTING GUIDELINES - Last updated: 31/5/2024<br>
© DJdj Development<br><br>
Website: https://www.dj-dj.be <br>
Discord: https://discord.dj-dj.be <br>
Email: support@dj-dj.be <br>
Documentation: https://otdocs.dj-dj.be
#### Step 5: Rewards
Translators get some rewards for translating Open Ticket!
- ✅ Credits in the [`README.md`](./../README.md) translator list!
- ✅ Credits in the changelog!
- ✅ Credits in the documentation!
- ✅ Credits in the translation JSON file!
- ✅ A special role in our [Discord server](https://discord.dj-dj.be)!
### 🧩 Plugins
#### Step 1: Creating Plugins
The documentation for creating plugins will be ready soon!<br>
In the meantime, you can already look at existing plugins or try it at your own!
#### Step 2: Uploading Plugins
Create a pull request in the [**`open-discord-plugins`**](https://odplugins.dj-dj.be/) repository or contact DJj123dj in our [Discord server](https://discord.dj-dj.be)!
### 📦 Features
Feature requests are required to be made in one of the following ways:
- ✅ In our [Discord server](https://discord.dj-dj.be)
- ✅ Create an issue on GitHub
- ✅ Message DJj123dj in DM on Discord
- ✅ Email to support@dj-dj.be
> **We won't accept pull requests for features. Only for bugs, small fixes, translation and plugins!**
### 🕷️ Bug Fixes
You are able to report bugs in one of the following ways:
- ✅ In our [Discord server](https://discord.dj-dj.be)
- ✅ Create an issue on GitHub
- ✅ Message DJj123dj in DM on Discord
- ✅ Email to support@dj-dj.be
📌 **If you want, you can also create a pull request to fix the bug yourself :)**
#### 🔴 Security Vulnerabilities
If you've found a bug which could affect the **SECURITY OF THE BOT** or may be potentially dangerous to the users of the bot,
you are **REQUIRED to send the bug privately via one of the following methods:**
- 🔴 Message DJj123dj in DM on Discord
- 🔴 Email to support@dj-dj.be
> ### Please try to always include the `otdebug.txt` file!
---
<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) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2021 - 2026 - [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)
+93
View File
@@ -0,0 +1,93 @@
{
"_INFO":"This document contains all Open Ticket contributors.",
"SPONSOR_TEMPLATE":{
"name":"INSERT_NAME",
"pictureUrl":"INSERT_PICTURE_URL",
"profileUrl":"INSERT_PROFILE_URL",
"sectionId":"INSERT_ID"
},
"SECTION_TEMPLATE":{
"name":"INSERT_NAME",
"id":"INSERT_ID",
"pfpSize":50,
"pfpColumns":5,
"withNames":true
},
"sections":[
{
"name":"🛠️ Contributors",
"id":"contributor",
"pfpSize":100,
"pfpColumns":8,
"withNames":true
},
{
"name":"📞 Discord Support",
"id":"support",
"pfpSize":100,
"pfpColumns":8,
"withNames":true
},
{
"name":"🧩 Plugin Developers",
"id":"plugin-dev",
"pfpSize":100,
"pfpColumns":8,
"withNames":true
},
{
"name":"💬 Translators",
"id":"translator",
"pfpSize":100,
"pfpColumns":8,
"withNames":true
}
],
"contributors":[
{"sectionId":"contributor","name":"DJj123dj","pictureUrl":"https://github.com/DJj123dj.png","profileUrl":"https://github.com/DJj123dj"},
{"sectionId":"contributor","name":"guillee3","pictureUrl":"https://github.com/guillee3.png","profileUrl":"https://github.com/guillee3"},
{"sectionId":"contributor","name":"SKaranjaN","pictureUrl":"https://github.com/SKaranjaN.png","profileUrl":"https://github.com/SKaranjaN"},
{"sectionId":"contributor","name":"Ashish5180","pictureUrl":"https://github.com/Ashish5180.png","profileUrl":"https://github.com/Ashish5180"},
{"sectionId":"contributor","name":"duboiss","pictureUrl":"https://github.com/duboiss.png","profileUrl":"https://github.com/duboiss"},
{"sectionId":"contributor","name":"sdehaarte","pictureUrl":"https://github.com/sdehaarte.png","profileUrl":"https://github.com/sdehaarte"},
{"sectionId":"contributor","name":"MauroDruwel","pictureUrl":"https://github.com/MauroDruwel.png","profileUrl":"https://github.com/MauroDruwel"},
{"sectionId":"support","name":"smetsliam","pictureUrl":"https://github.com/smetsliam.png","profileUrl":"https://github.com/smetsliam"},
{"sectionId":"support","name":"Sank34","pictureUrl":"https://github.com/Sank34.png","profileUrl":"https://github.com/Sank34"},
{"sectionId":"support","name":"FrankVissers","pictureUrl":"https://github.com/FrankVissers.png","profileUrl":"https://github.com/FrankVissers"},
{"sectionId":"plugin-dev","name":"Rapid-Fast","pictureUrl":"https://github.com/Rapid-Fast.png","profileUrl":"https://github.com/Rapid-Fast"},
{"sectionId":"plugin-dev","name":"NotMukundOP","pictureUrl":"https://github.com/NotMukundOP.png","profileUrl":"https://github.com/NotMukundOP"},
{"sectionId":"plugin-dev","name":"Imperatorix17","pictureUrl":"https://github.com/imperatorix17.png","profileUrl":"https://github.com/imperatorix17"},
{"sectionId":"plugin-dev","name":"DanoGlez","pictureUrl":"https://github.com/DanoGlez.png","profileUrl":"https://github.com/DanoGlez"},
{"sectionId":"plugin-dev","name":"yowsef","pictureUrl":"https://github.com/Yow-sef.png","profileUrl":"https://github.com/Yow-sef"},
{"sectionId":"plugin-dev","name":"MeneerSouf","pictureUrl":"https://github.com/MeneerSouf.png","profileUrl":"https://github.com/MeneerSouf"},
{"sectionId":"plugin-dev","name":"challenger_nova","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"HanumeshGupta","pictureUrl":"https://github.com/HanumeshGupta.png","profileUrl":"https://github.com/HanumeshGupta"},
{"sectionId":"translator","name":"benzorich","pictureUrl":"https://github.com/benzorich.png","profileUrl":"https://github.com/benzorich"},
{"sectionId":"translator","name":"Reddishye","pictureUrl":"https://github.com/Reddishye.png","profileUrl":"https://github.com/Reddishye"},
{"sectionId":"translator","name":"josuens","pictureUrl":"https://github.com/josuens.png","profileUrl":"https://github.com/josuens"},
{"sectionId":"translator","name":"quiradon","pictureUrl":"https://github.com/quiradon.png","profileUrl":"https://github.com/quiradon"},
{"sectionId":"translator","name":"Imperatorix17","pictureUrl":"https://github.com/imperatorix17.png","profileUrl":"https://github.com/imperatorix17"},
{"sectionId":"translator","name":"NoOneNook","pictureUrl":"https://github.com/NoOneNook.png","profileUrl":"https://github.com/NoOneNook"},
{"sectionId":"translator","name":"guillee3","pictureUrl":"https://github.com/guillee3.png","profileUrl":"https://github.com/guillee3"},
{"sectionId":"translator","name":"Mods HD","pictureUrl":"https://github.com/mods-hd.png","profileUrl":"https://github.com/mods-hd"},
{"sectionId":"translator","name":"anderskiy","pictureUrl":"https://github.com/anderskiy.png","profileUrl":"https://github.com/anderskiy"},
{"sectionId":"translator","name":"SpyEye2","pictureUrl":"https://github.com/SpyEye2.png","profileUrl":"https://github.com/SpyEye2"},
{"sectionId":"translator","name":"Sank34","pictureUrl":"https://github.com/Sank34.png","profileUrl":"https://github.com/Sank34"},
{"sectionId":"translator","name":"thegamer5095","pictureUrl":"https://github.com/thegamer5095.png","profileUrl":"https://github.com/thegamer5095"},
{"sectionId":"translator","name":"danoglez","pictureUrl":"https://github.com/danoglez.png","profileUrl":"https://github.com/danoglez"},
{"sectionId":"translator","name":"zhavis","pictureUrl":"https://github.com/zhavis.png","profileUrl":"https://github.com/zhavis"},
{"sectionId":"translator","name":"yuuslokrobjakkroval","pictureUrl":"https://github.com/yuuslokrobjakkroval.png","profileUrl":"https://github.com/yuuslokrobjakkroval"},
{"sectionId":"translator","name":"imLudwig","pictureUrl":"https://github.com/imLudwig.png","profileUrl":"https://github.com/imLudwig"},
{"sectionId":"translator","name":"iamnotmega","pictureUrl":"https://github.com/iamnotmega.png","profileUrl":"https://github.com/iamnotmega"},
{"sectionId":"translator","name":"Ronalds13424","pictureUrl":"https://github.com/Ronalds13424.png","profileUrl":"https://github.com/Ronalds13424"},
{"sectionId":"translator","name":"dysashop","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"fraden1mvp.","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"palestinian","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"challenger_nova","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"erxg","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"tsgindrius","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"kornel0706","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"me.october","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""},
{"sectionId":"translator","name":"ngocdiep2006","pictureUrl":"https://apis.dj-dj.be/cdn/openticket/default-avatar.png","profileUrl":""}
]
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.4 MiB

+14 -21
View File
@@ -1,37 +1,30 @@
---
name: Bug report
about: Report bugs to help us improve open ticket!
title: "[BUG] your bug"
labels: bug
assignees: DJj123dj
name: Bug Report
about: Report bugs to help us improve Open Ticket!
title: "Your Bug Name"
---
**Describe the bug**
### Bug Description:
A clear and concise description of what the bug is.
**To Reproduce**
### Steps To Reproduce:
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
> Leave empty when you are unable to reproduce the bug!
### Expected Behaviour:
A clear and concise description of what you expected to happen.
**Screenshots**
### Screenshots:
If applicable, add screenshots to help explain your problem.
**Device:**
- Device: [e.g. Macbook pro, raspberry pi, HP Probook]
- OS: [e.g. MacOS, Ubuntu, Windows,...]
- Node.js version:
- Open Ticket Version:
### Files:
Please include the `otdebug.txt` file in your report! With this file, we can take a deep-dive in to the error that happend.
> This file doesn't contain any credentials or sensitive information!
**Files:**
Please include the `openticketdebug.txt` file in your report!
This is a file that we can see what happend!
**Additional context**
Add any other context about the problem here.
### Additional Notes:
Add any other context about the problem here.
+27
View File
@@ -0,0 +1,27 @@
---
name: Documentation
about: Do you know anything to be improved in the Open Ticket documentation?
title: "Documentation Improvement Name"
labels: "Documentation"
---
### Related Docs/Systems:
Choose related systems/docs for this improvement/change:
- [ ] Guides
- [ ] Developer Guides
- [ ] API Reference
- [ ] Blog
- [ ] System (Docusaurus & Javascript)
- [ ] Assets (images)
- [ ] Other
### Improvement Description:
A clear and concise description of the improvement.
> Please explain it as detailed as possible!
### Screenshots:
If applicable, add screenshots to help explain your request.
### Additional Notes:
Add any other context about the request here.
+27
View File
@@ -0,0 +1,27 @@
---
name: Feature Request
about: Do you have a great idea for Open Ticket? Let us know using this issue!
title: "Your Feature Name"
labels: "Concept"
---
### Feature Description:
A clear and concise description of what the feature is.
### Affected Systems
Systems which are affected, improved or modified:
- (example) Slash command registration...
- (example) API embed builders...
- Scroll down to '....'
- See error
> Leave empty when you don't know which parts this feature affects!
### Advantages & Use Cases:
What advantages & use cases do you think this feature would result in?
### Screenshots:
If applicable, add screenshots to help explain your feature request.
### Additional Notes:
Add any other context about the request here.
@@ -0,0 +1,20 @@
---
name: HTML Transcripts
about: Found a bug related to the HTML Transcripts? Or do you have a feature request?
title: "Your Bug/Improvement Name"
labels: "HTML Transcripts"
---
### Feature/Bug Description:
A clear and concise description of what the feature/bug report is.
### Screenshots:
If applicable, add screenshots to help explain your feature request or bug report.
### Files:
Please include the `otdebug.txt` file in your report! With this file, we can take a deep-dive in to the error that happend.
> #### Include this file for bug reports!
> This file doesn't contain any credentials or sensitive information!
### Additional Notes:
Add any other context about the problem here.
+30
View File
@@ -0,0 +1,30 @@
---
name: New Language
about: Do you want to add a new language to Open Ticket?
title: "Language Name"
labels: "New Translation"
---
### Language Name
The name of the new language.
### Language Description/Details:
A clear and concise description of the translation/language.
### Checklist
Before the language is fully implemented, the following checklist must be met:
- [ ] **📌 Created a language file in the `./languages/` directory.**
- [ ] **📌 The translation file uses the `<lowercase-language-name>.json` format.**
- [ ] **📌 The `"_TRANSLATION"` metadata in the language file is correctly configured.**
- [ ] The `"otversion"` is correct.
- [ ] The `"translators"` list is up to date has the discord/github usernames from all translators.
- [ ] The `"lastedited"` variable is the last edited date in the following format: `DD/MM/YYYY`.
- [ ] The `"language"` variable is the name of the language in English.
- [ ] `"automated"` is enabled when `Google Translate`, `DeepL`, `ChatGPT` or any other kind of translation tools were used.
- [ ] **📌 The language has been added to the `README.md` list.**
- [ ] **📌 The language counters have been updated in the `README.md`.**
- [ ] **📌 The language has been added to the list in the `ODLanguageManagerIds_Default` interface in (`./src/core/api/defaults/language.ts`).**
- [ ] **📌 The language has been added to the list in `./src/data/languageLoader.ts`.**
### Additional Notes:
Add any other context about the problem here.
-29
View File
@@ -1,29 +0,0 @@
---
name: Plugin
about: Show your plugin to the developers from open ticket!
title: "[PLUGIN] your plugin"
labels: plugin
assignees: DJj123dj
---
**Plugin Name:**
What's the name of your plugin?
**Description:**
Tell a little bit about what the plugin does!
Also tell how to use the plugin, like if there is additional setup needed!
**NPM libraries:**
List all the libraries needed for this plugin to work!
*The default open ticket libraries are not required!*
**Author:**
- github account [e.g. DJj123dj]
- discord account [e.g. Wumpus#1234]
**Compatible versions:**
[e.g. from v2.2.1 until v3.0.1]
**File:**
Please attach the plugin file to this issue!
+20
View File
@@ -0,0 +1,20 @@
---
name: Plugin Request
about: Do you have an idea for an Open Ticket plugin?
title: "Your Plugin Name"
labels: "Plugin"
---
### Plugin Description:
A clear and concise description of what the plugin is & does.
> Please explain the plugin as detailed as possible!
### Advantages & Use Cases:
What advantages & use cases do you think this plugin would result in?
### Screenshots:
If applicable, add screenshots to help explain your plugin request.
### Additional Notes:
Add any other context about the request here.
+28
View File
@@ -0,0 +1,28 @@
---
name: Question
about: Do you have a question about Open Ticket?
title: "My Question"
labels: "Question"
---
### Question:
A clear and concise description of the question.
> Please explain the question as detailed as possible!
### Screenshots:
If applicable, add screenshots to help explain your plugin request.
### Checklist:
Before creating this question, the following checklist must be met:
- [ ] **📌 I've read the docs (https://otdocs.dj-dj.be) and I didn't found the solution.**
- [ ] **📌 I've asked other people & they didn't know the solution.**
- [ ] **📌 The question is not related to the following subjects: (If so, ask it in our discord server: https://discord.dj-dj.be)**
- Configuring the bot
- Hosting the bot (24/7)
- Updating the bot
- [ ] **📌 I know that this issue is publicly visible for everyone and I haven't shared any private info.**
### Additional Notes:
Add any other context about the request here.
@@ -0,0 +1,24 @@
---
name: Updated Language
about: Do you want to update/improve an existing language in Open Ticket?
title: "Language Name"
labels: "Updated Translation"
---
### Language Name
The name of the existing language.
### Language Description/Details:
A clear and concise description of the translation/language changes.
### Checklist
Before the language is updated, the following checklist must be met:
- [ ] **📌 The `"_TRANSLATION"` metadata in the language file is correctly configured.**
- [ ] The `"otversion"` is correct.
- [ ] The `"translators"` list is up to date has the discord/github usernames from all translators.
- [ ] The `"lastedited"` variable is the last edited date in the following format: `DD/MM/YYYY`.
- [ ] The `"language"` variable is the name of the language in English.
- [ ] `"automated"` is enabled when `Google Translate`, `DeepL`, `ChatGPT` or any other kind of translation tools were used.
### Additional Notes:
Add any other context about the problem here.
+42 -30
View File
@@ -1,40 +1,52 @@
# Security Policy
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="500px">
## Supported Versions
Below, you can find a list with the status of every open ticket version. This list will change every update!
[![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.4-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.0.4) [![Sponsor DJj123dj](https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors)](https://github.com/sponsors/DJj123dj)
- ✅ Supported **(bugs, errors, discord support, documentation, html transcripts)**
- 🟧 Deprecated / Partially Supported **(discord support, documentation, html transcripts)**
- ❌ Fully Deprecated / Not Supported **(sometimes documentation)**
This is the Security Policy of Open Ticket!<br>
Here you can find a list of all supported & deprecated versions of the bot.<br>
You will also find some guidelines about how to report vulnerabilities, bugs & errors.
| Version | Supported | Until |
|-----------|-----------|-----------------------------|
| 3.5.7 | ✅ | |
| 3.5.6 | ✅ | August 2024 |
| 3.5.5 | 🟧 | Next Update |
| < 3.5.5 | ❌ | |
### 📦 Supported Versions
Below, you can find a list with the status of every Open Ticket version.<br>
This list will be updated on every release.
## Reporting a Vulnerability
- 🟦 In Development **(ideas, bugs, support, html transcripts)**
- ✅ Supported **(features, bugs, support, docs, html transcripts)**
- 🚧 Maintenance **(support, docs, html transcripts)**
- 🟧 Deprecated **(docs only)**
- ❌ Fully Deprecated
You can report Vulnerabilities, Errors & Bugs using one of the methods below:
- Create an issue on github
- Create a ticket in our [discord server](https://discord.dj-dj.be)
- Report the bug in DM on discord (djj123dj)
- Email [support@dj-dj.be](mailto:support@dj-dj.be)
| Version | Supported | Notes |
|------------|-----------|---------------------------------------------------------------|
| 4.2.x | 🟦 | In Development |
| 4.2.1 | 🟦 | In Development |
| 4.2.0 | ✅ | |
| 4.1.3 | ✅ | (LTS) Long-Term-Support, Until September 2026 |
| 4.1.2 | 🚧 | |
| 4.1.1 | 🚧 | |
| 4.1.0 | 🚧 | |
| 4.0.7 | 🟧 | Deprecated |
| < 4.0.7 | ❌ | |
### 🕷️ Reporting Vulnerabilities
You can report vulnerabilities, errors & bugs using one of the following methods:
- Create an issue on GitHub!
- Create a ticket in our [discord server](https://discord.dj-dj.be)!
- Message @DJj123dj in DM on discord!
- Send an E-mail to [support@dj-dj.be](mailto:support@dj-dj.be)!
When you report a problem, please **give a small description** about what & how it happend.<br>
Try to explain the issue in as much detail as possible.
While reporting your problem, please be clear about what the problem is.
We would like to know the issue in as much detail as possible.
If possible, try to provide screenshots!
The `openticketdebug.txt` file would also be handy to upload!<br>
**Always specify the version of open ticket that you are working with and if you have modified it or not!**
## Contributing
Visit [Contributing.md](/.github/CONTRIBUTING.md) for rules about translating & adding features!
### Please upload the `otdebug.txt` file together with your bug report!
<br>
SECURITY POLICY - Last updated: 2/7/2024<br>
© DJdj Development<br><br>
Website: https://www.dj-dj.be <br>
Discord: https://discord.dj-dj.be <br>
Email: support@dj-dj.be <br>
Open Ticket website: https://openticket.dj-dj.be
---
<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) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2021 - 2026 - [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)
+90
View File
@@ -0,0 +1,90 @@
{
"_INFO":"This document contains all Open Ticket sponsors.",
"SPONSOR_TEMPLATE":{
"name":"INSERT_NAME",
"pictureUrl":"INSERT_PICTURE_URL",
"profileUrl":"INSERT_PROFILE_URL",
"sectionId":"INSERT_ID"
},
"SECTION_TEMPLATE":{
"name":"INSERT_NAME",
"id":"INSERT_ID",
"pfpSize":50,
"pfpColumns":5,
"withNames":true
},
"sections":[
{
"name":"💎 Platinum Sponsors",
"id":"platinum",
"pfpSize":120,
"pfpColumns":4,
"withNames":true
},
{
"name":"🥇 Gold Sponsors",
"id":"gold",
"pfpSize":60,
"pfpColumns":8,
"withNames":true
},
{
"name":"🥈 Silver Sponsors",
"id":"silver",
"pfpSize":40,
"pfpColumns":16,
"withNames":false
},
{
"name":"❤️ Past Sponsors",
"id":"past",
"pfpSize":30,
"pfpColumns":20,
"withNames":false
}
],
"sponsors":[
{
"name":"Guillee3",
"pictureUrl":"https://github.com/guillee3.png",
"profileUrl":"https://github.com/guillee3",
"sectionId":"platinum"
},
{
"name":"Jacob Humston",
"pictureUrl":"https://github.com/jacobhumston.png",
"profileUrl":"https://github.com/jacobhumston",
"sectionId":"platinum"
},
{
"name":"SpyEye2",
"pictureUrl":"https://github.com/SpyEye2.png",
"profileUrl":"https://github.com/SpyEye2",
"sectionId":"gold"
},
{
"name":"DOSEV5",
"pictureUrl":"https://github.com/DOSEV5.png",
"profileUrl":"https://github.com/DOSEV5",
"sectionId":"gold"
},
{
"name":"YeeetSK",
"pictureUrl":"https://github.com/YeeetSK.png",
"profileUrl":"https://github.com/YeeetSK",
"sectionId":"silver"
},
{
"name":"BENZORICH",
"pictureUrl":"https://github.com/BENZORICH.png",
"profileUrl":"https://github.com/BENZORICH",
"sectionId":"silver"
},
{
"name":"Mods HD",
"pictureUrl":"https://github.com/mods-hd.png",
"profileUrl":"https://github.com/mods-hd",
"sectionId":"silver"
}
]
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 124 KiB

+53
View File
@@ -0,0 +1,53 @@
# Pterodactyl Eggs
<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.2.0-brightgreen.svg?style=flat-square)](https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0)
[![Sponsor DJj123dj](https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors)](https://github.com/sponsors/DJj123dj)
[![Open Ticket supports Pterodactyl Eggs!](https://img.shields.io/badge/pterodactyl-supported-10539F?style=flat-square&logo=pterodactyl)](.eggs/README.md)
Hi there! Open Ticket provides **official eggs** for the Pterodactyl & Pelican panels!<br>
There are different eggs for different versions of Open Ticket.
Please choose the one that fits your needs the most.
If you encounter any issues while installing these eggs, please head to our [**discord server**](https://discord.dj-dj.be) for further assistance!
### Requirements
It's recommended to provide at least `1GB` of **Memory/RAM** and `5GB` of **disk space** for Open Ticket to work correctly.
### Egg Variants
[**`openticket-egg-main.json` (Recommended)**](openticket-egg-main.json)
- This egg will use the `main` branch of Open Ticket.
[**`openticket-egg-v4.2.0.json`**](openticket-egg-v4.2.0.json)
- This egg will always use Open Ticket `v4.2.0`. Open Ticket updates will not have an effect on this egg.
[**`openticket-egg-v4.1.3.json`**](openticket-egg-v4.1.3.json)
- This egg will always use Open Ticket `v4.1.3`. Open Ticket updates will not have an effect on this egg.
[**`openticket-egg-v4.1.2.json`**](openticket-egg-v4.1.2.json)
- This egg will always use Open Ticket `v4.1.2`. Open Ticket updates will not have an effect on this egg.
[**`openticket-egg-v4.1.1.json`**](openticket-egg-v4.1.1.json)
- This egg will always use Open Ticket `v4.1.1`. Open Ticket updates will not have an effect on this egg.
[**`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.
[**`openticket-egg-v3.5.9.json`**](openticket-egg-v3.5.9.json)
- This egg will always use Open Ticket `v3.5.9`. Open Ticket updates will not have an effect on this egg.
[**`openticket-egg-dev.json` (Not Recommended)**](openticket-egg-dev.json)
- This egg will use the `dev` branch of Open Ticket.
---
<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) - [Website](https://openticket.dj-dj.be) - [Support Server](https://discord.dj-dj.be) - [License](./LICENSE.md)<br>
© 2021 - 2026 - [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)
@@ -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:21+01:00",
"name": "Open Ticket (Experimental)",
"author": "support@dj-dj.be",
"description": "This is the experimental Pterodactyl egg for Open Ticket, the most advanced & customisable discord ticket bot that you will ever find! DO NOT USE IN PRODUCTION!",
"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 (dev)\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=\"dev\"\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"
}
]
}
@@ -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:20+01:00",
"name": "Open Ticket (Latest)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket, 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 (main)\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=\"main\"\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"
}
]
}
@@ -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:19+01:00",
"name": "Open Ticket (v3.5.9)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v3.5.9, 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 (v3.5.9)\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=\"v3.5.9\"\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"
}
]
}
@@ -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.0.7)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v4.0.7, 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.0.7)\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.0.7\"\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"
}
]
}
@@ -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"
}
]
}
@@ -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.1)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v4.1.1, 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.1)\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.1\"\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"
}
]
}
@@ -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.2)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v4.1.2, 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.2)\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.2\"\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"
}
]
}
@@ -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.3)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v4.1.3, 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.3)\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.3\"\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"
}
]
}
@@ -0,0 +1,62 @@
{
"_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
"meta": {
"version": "PTDL_v2",
"update_url": null
},
"exported_at": "2025-03-16T18:10:18+01:00",
"name": "Open Ticket (v4.2.0)",
"author": "support@dj-dj.be",
"description": "This is the official Pterodactyl egg for Open Ticket v4.2.0, the most advanced & customisable discord ticket bot that you will ever find! You can customise up to 300+ variables! This includes Html Transcripts, Advanced Plugins, Custom Embeds, Questions\/Modals, Stats & more!",
"features": null,
"docker_images": {
"ghcr.io\/parkervcp\/yolks:nodejs_20": "ghcr.io\/parkervcp\/yolks:nodejs_20"
},
"file_denylist": [],
"startup": "if [[ ! -z ${NODE_PACKAGES} ]]; then npm install ${NODE_PACKAGES}; fi; if [[ ! -z ${UNNODE_PACKAGES} ]]; then npm uninstall ${UNNODE_PACKAGES}; fi; if [ -f \/home\/container\/package.json ]; then npm install; fi; node \"\/home\/container\/index.js\" ${NODE_FLAGS};",
"config": {
"files": "{}",
"startup": "{\r\n \"done\": \"STARTUP INFO:\"\r\n}",
"logs": "{}",
"stop": "^C"
},
"scripts": {
"installation": {
"script": "#!\/bin\/bash\r\n# Open Ticket Installation Script (v4.2.0)\r\n# Inspired by: Node.js Installation Script\r\n# \u00a9 DJdj Development\r\n\r\necho -e \"[OT INSTALLER] installing dependencies. please wait...\"\r\napt update\r\napt install -y git curl\r\n\r\necho -e \"[OT INSTALLER] updating npm. please wait...\"\r\nnpm install npm@latest --location=global\r\n\r\nmkdir -p \/mnt\/server\r\ncd \/mnt\/server\r\n\r\nBRANCH=\"v4.2.0\"\r\n\r\nif [ \"$(ls -A \/mnt\/server)\" ]; then\r\n echo -e \"[OT INSTALLER] \/mnt\/server directory is not empty.\"\r\n if [ -d .git ]; then\r\n echo -e \"[OT INSTALLER] .git directory exists\"\r\n if [ -f .git\/config ]; then\r\n echo -e \"[OT INSTALLER] loading info from git config\"\r\n ORIGIN=$(git config --get remote.origin.url)\r\n else\r\n echo -e \"[OT INSTALLER] files found with no git config\"\r\n echo -e \"[OT INSTALLER] closing out without touching things to not break anything\"\r\n exit 10\r\n fi\r\n fi\r\n\r\n if [ \"${ORIGIN}\" == \"https:\/\/github.com\/open-discord-bots\/open-ticket.git\" ]; then\r\n echo \"pulling latest from github\"\r\n git pull\r\n fi\r\nelse\r\n echo -e \"[OT INSTALLER] \/mnt\/server is empty.\"\r\n echo -e \"[OT INSTALLER] cloning files into repo.\"\r\n if [ -z ${BRANCH} ]; then\r\n echo -e \"[OT INSTALLER] cloning default branch\"\r\n git clone https:\/\/github.com\/open-discord-bots\/open-ticket.git .\r\n else\r\n echo -e \"[OT INSTALLER] cloning ${BRANCH}'\"\r\n git clone --single-branch --branch ${BRANCH} https:\/\/github.com\/open-discord-bots\/open-ticket.git .\r\n fi\r\nfi\r\n\r\necho -e \"[OT INSTALLER] installing Open Ticket node.js packages.\"\r\nif [ -f \/mnt\/server\/package.json ]; then\r\n \/usr\/local\/bin\/npm install --omit=dev\r\nfi\r\n\r\necho -e \"[OT INSTALLER] installing custom node.js packages.\"\r\nif [[ ! -z ${NODE_PACKAGES} ]]; then\r\n \/usr\/local\/bin\/npm install ${NODE_PACKAGES}\r\nfi\r\n\r\necho -e \"[OT INSTALLER] install complete!\"\r\nexit 0",
"container": "node:latest",
"entrypoint": "bash"
}
},
"variables": [
{
"name": "Additional Npm Packages",
"description": "Specify additional npm packages used by Open Ticket plugins. Use spaces to separate.",
"env_variable": "NODE_PACKAGES",
"default_value": "",
"user_viewable": false,
"user_editable": true,
"rules": "string|nullable",
"field_type": "text"
},
{
"name": "Startup Flags",
"description": "Start the bot with additional Open Ticket flags. Separate using spaces.\r\nA full reference list can be found in the documentation.",
"env_variable": "NODE_FLAGS",
"default_value": "",
"user_viewable": false,
"user_editable": true,
"rules": "string|nullable",
"field_type": "text"
},
{
"name": "Uninstall Npm Packages",
"description": "A list of npm packages to uninstall. Separate by spaces.",
"env_variable": "UNNODE_PACKAGES",
"default_value": "",
"user_viewable": false,
"user_editable": true,
"rules": "string|nullable",
"field_type": "text"
}
]
}
+22 -19
View File
@@ -1,23 +1,26 @@
node_modules/
package-lock.json
devconfig.json
devconfig1.json
devconfig2.json
devconfig3.json
devconfig4.json
devconfig5.json
.DS_Store
openticketdebug.txt
developerChangelog.md
storage/dynamicDB/
storage/slashcmdEnabled.txt
storage/database.json
storage/tsdatabase.json
storage/stats.json
storage/.DS_Store
.vscode/
livestatus.json
test.js
devtsconfig.json
devconfig/
devdatabase/
plugins/*
!plugins/example.plugin.js
!plugins/example-plugin/
database/openticket.sqlite
dist/
otdebug.txt
.DS_Store
*/.DS_Store
*/*/.DS_Store
*/*/*/.DS_Store
**/.DS_Store
.backup/*
.tools/*
!.tools/createSponsors.ts
!.tools/createContributors.ts
!.tools/mergeTranslations.ts
!.tools/docker-compose.yml
!.tools/build-docker.sh
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# build.sh — Build Open Ticket Docker images for all supported architectures.
#
# Usage: ./build.sh <image> [--no-push]
# image Full image name, e.g. djj123dj/open-ticket
#
# Example: ./build.sh djj123dj/open-ticket --no-push
set -euo pipefail
cd "$(dirname "$0")/.."
echo "✅ Switched working directory to Open Ticket root"
pwd
IMAGE="${1:?Usage: ./build-docker.sh <image> [--no-push]}"
PLATFORMS="linux/amd64,linux/arm64" # linux/arm/v6,linux/arm/v7,linux/s390x are taking too long to build, so we'll skip them for now
VERSION=$(node -p "require('./package.json').version")
# Create a multi-arch builder if it doesn't exist yet
docker buildx inspect ot-builder &>/dev/null \
|| docker buildx create --name ot-builder --driver docker-container --bootstrap --use
docker buildx use ot-builder
PUSH_FLAG="--push"
[[ "${2:-}" == "--no-push" ]] && PUSH_FLAG=""
docker buildx build \
--platform "$PLATFORMS" \
--tag "${IMAGE}:v${VERSION}" \
--tag "${IMAGE}:latest" \
--load \
$PUSH_FLAG \
.
[[ -n "$PUSH_FLAG" ]] \
&& echo "✅ Pushed ${IMAGE}:v${VERSION} and ${IMAGE}:latest" \
|| echo "✅ Built ${IMAGE}:v${VERSION} (not pushed)"
+130
View File
@@ -0,0 +1,130 @@
/// <reference types="node"/>
import fs from "fs"
import path from "path"
import crypto from "crypto"
const contributorData: {contributors:Contributor[],sections:Section[]} = JSON.parse(fs.readFileSync(path.join(process.cwd(),"./.github/CONTRIBUTORS.json")).toString())
//CONSTANTS
const CORNER_RADIUS = 10
const SPACE_MULTIPLIER = 1.2
const SVG_WIDTH = 1000
//TYPES
interface Contributor {
name:string,
pictureUrl:string,
profileUrl:string,
sectionId:string
}
interface Section {
name:string,
id:string,
pfpSize:number,
pfpColumns:number,
withNames:boolean
}
//FUNCTIONS
async function downloadPfpToBase64URL(url:string){
const res = await fetch(url,{method:"GET"})
if (!res.ok) return null
const buffer = Buffer.from(await res.arrayBuffer())
console.log("Downloaded picture URL:",url)
return "data:image/png;base64,"+buffer.toString("base64")
}
function createTitle(yPos:number,name:string){
return `<text x="${20}" y="${yPos+20}" text-anchor="start" class="contributor-tier-title">${name}</text>`
}
async function createPfp(yPos:number,xPos:number,size:number,contributor:Contributor,withNames:boolean){
const randomId = crypto.randomBytes(8).toString("hex")
const nameElement = (withNames) ? `<text x="${Math.round(xPos+(size/2))}" y="${yPos+size+20}" text-anchor="middle" fill="currentColor">${contributor.name}</text>` : ""
return (`<a href="${contributor.profileUrl}" class="contributor-link" target="_blank">
<clipPath id="clipPath-${randomId}">
<rect x="${xPos}" y="${yPos}" width="${size}" height="${size}" rx="${CORNER_RADIUS}" ry="${CORNER_RADIUS}"/>
</clipPath>
<image x="${xPos}" y="${yPos}" width="${size}" height="${size}" href="${await downloadPfpToBase64URL(contributor.pictureUrl)}" clip-path="url(#clipPath-${randomId})"/>
${nameElement}
</a>`)
}
async function generateSection(yPos:number,section:Section,contributors:Contributor[]){
let sectionHtml: string = ""
sectionHtml += createTitle(yPos,section.name)
const nameOffset = (section.withNames) ? 20 : 0
//divide contributors in rows
const groupedContributors: Contributor[][] = []
let currentGroup: Contributor[] = []
for (const contributor of contributors){
currentGroup.push(contributor)
if (currentGroup.length == section.pfpColumns){
groupedContributors.push(currentGroup)
currentGroup = []
}
}
if (currentGroup.length > 0) groupedContributors.push(currentGroup)
let y = 0
for (const contributorGroup of groupedContributors){
let x = 0
for (const contributor of contributorGroup){
const pfpYPos = 40 + yPos + (y * ((section.pfpSize * SPACE_MULTIPLIER) + nameOffset))
const pfpXPos = 20 + (x * section.pfpSize * SPACE_MULTIPLIER)
sectionHtml += await createPfp(pfpYPos,pfpXPos,section.pfpSize,contributor,section.withNames)
x++
}
y++
}
let sectionHeight: number = 40 + (y * ((section.pfpSize * SPACE_MULTIPLIER) + nameOffset))
return {sectionHtml,sectionHeight}
}
async function generateSections(sections:Section[],contributors:Contributor[]){
let finalHeight: number = 10
let finalHtml: string = ""
for (const section of sections){
const sectionContributors = contributors.filter((s) => s.sectionId === section.id)
if (sectionContributors.length < 1) continue
const {sectionHtml,sectionHeight} = await generateSection(finalHeight,section,sectionContributors)
finalHeight += sectionHeight
finalHtml += sectionHtml
}
finalHeight += 10
return {finalHeight,finalHtml}
}
function generateFinalHtml(sectionsHtml:string,sectionHeight:number){
return (`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${SVG_WIDTH} ${sectionHeight}" width="${SVG_WIDTH}" height="${sectionHeight}">
<style>
text {
font-weight: 300;
font-size: 14px;
fill: #777777;
font-family: 'Open Sans', 'Helvetica Neue', sans-serif;
}
.contributor-link {
cursor: pointer;
}
.contributor-tier-title {
font-weight: 500;
font-size: 20px;
}
</style>
${sectionsHtml}
</svg>`)
}
//GENERATE CONTRIBUTORS SVG
async function main(){
const {finalHeight,finalHtml} = await generateSections(contributorData.sections,contributorData.contributors)
fs.writeFileSync(path.join(process.cwd(),"./.github/CONTRIBUTORS.svg"),generateFinalHtml(finalHtml,finalHeight))
}
main()
+132
View File
@@ -0,0 +1,132 @@
/// <reference types="node"/>
import fs from "fs"
import path from "path"
import crypto from "crypto"
const sponsorData: {sponsors:Sponsor[],sections:Section[]} = JSON.parse(fs.readFileSync(path.join(process.cwd(),"./.github/SPONSORS.json")).toString())
//CONSTANTS
const CORNER_RADIUS = 10
const SPACE_MULTIPLIER = 1.2
const SVG_WIDTH = 1000
//TYPES
interface Sponsor {
name:string,
pictureUrl:string,
profileUrl:string,
sectionId:string
}
interface Section {
name:string,
id:string,
pfpSize:number,
pfpColumns:number,
withNames:boolean
}
//FUNCTIONS
async function downloadPfpToBase64URL(url:string){
const res = await fetch(url,{method:"GET"})
if (!res.ok) return null
const buffer = Buffer.from(await res.arrayBuffer())
console.log("Downloaded picture URL:",url)
return "data:image/png;base64,"+buffer.toString("base64")
}
function createTitle(yPos:number,name:string){
return `<text x="${Math.round(SVG_WIDTH/2)}" y="${yPos+20}" text-anchor="middle" class="sponsor-tier-title">${name}</text>`
}
async function createPfp(yPos:number,xPos:number,size:number,sponsor:Sponsor,withNames:boolean){
const randomId = crypto.randomBytes(8).toString("hex")
const nameElement = (withNames) ? `<text x="${Math.round(xPos+(size/2))}" y="${yPos+size+20}" text-anchor="middle" fill="currentColor">${sponsor.name}</text>` : ""
return (`<a href="${sponsor.profileUrl}" class="sponsor-link" target="_blank">
<clipPath id="clipPath-${randomId}">
<rect x="${xPos}" y="${yPos}" width="${size}" height="${size}" rx="${CORNER_RADIUS}" ry="${CORNER_RADIUS}"/>
</clipPath>
<image x="${xPos}" y="${yPos}" width="${size}" height="${size}" href="${await downloadPfpToBase64URL(sponsor.pictureUrl)}" clip-path="url(#clipPath-${randomId})"/>
${nameElement}
</a>`)
}
async function generateSection(yPos:number,section:Section,sponsors:Sponsor[]){
let sectionHtml: string = ""
sectionHtml += createTitle(yPos,section.name)
const nameOffset = (section.withNames) ? 20 : 0
//divide sponsors in rows
const groupedSponsors: Sponsor[][] = []
let currentGroup: Sponsor[] = []
for (const sponsor of sponsors){
currentGroup.push(sponsor)
if (currentGroup.length == section.pfpColumns){
groupedSponsors.push(currentGroup)
currentGroup = []
}
}
if (currentGroup.length > 0) groupedSponsors.push(currentGroup)
let y = 0
for (const sponsorGroup of groupedSponsors){
const xOffset = Math.round((SVG_WIDTH - (sponsorGroup.length * section.pfpSize * SPACE_MULTIPLIER))/2)
let x = 0
for (const sponsor of sponsorGroup){
const pfpYPos = 40 + yPos + (y * ((section.pfpSize * SPACE_MULTIPLIER) + nameOffset))
const pfpXPos = xOffset + (x * section.pfpSize * SPACE_MULTIPLIER)
sectionHtml += await createPfp(pfpYPos,pfpXPos,section.pfpSize,sponsor,section.withNames)
x++
}
y++
}
let sectionHeight: number = 40 + (y * ((section.pfpSize * SPACE_MULTIPLIER) + nameOffset))
return {sectionHtml,sectionHeight}
}
async function generateSections(sections:Section[],sponsors:Sponsor[]){
let finalHeight: number = 10
let finalHtml: string = ""
for (const section of sections){
const sectionSponsors = sponsors.filter((s) => s.sectionId === section.id)
if (sectionSponsors.length < 1) continue
const {sectionHtml,sectionHeight} = await generateSection(finalHeight,section,sectionSponsors)
finalHeight += sectionHeight
finalHtml += sectionHtml
}
finalHeight += 10
return {finalHeight,finalHtml}
}
function generateFinalHtml(sectionsHtml:string,sectionHeight:number){
return (`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 ${SVG_WIDTH} ${sectionHeight}" width="${SVG_WIDTH}" height="${sectionHeight}">
<style>
text {
font-weight: 300;
font-size: 14px;
fill: #777777;
font-family: 'Open Sans', 'Helvetica Neue', sans-serif;
}
.sponsor-link {
cursor: pointer;
}
.sponsor-tier-title {
font-weight: 500;
font-size: 20px;
}
</style>
<rect x="2" y="2" width="${SVG_WIDTH-4}" height="${sectionHeight-4}" rx="20" ry="20" style="fill:transparent;stroke:#f8ba00;stroke-width:3"></rect>
${sectionsHtml}
</svg>`)
}
//GENERATE SPONSORS SVG
async function main(){
const {finalHeight,finalHtml} = await generateSections(sponsorData.sections,sponsorData.sponsors)
fs.writeFileSync(path.join(process.cwd(),"./.github/SPONSORS.svg"),generateFinalHtml(finalHtml,finalHeight))
}
main()
+14
View File
@@ -0,0 +1,14 @@
# Docker Compose for Open Ticket
services:
openticket:
image: djj123dj/open-ticket:latest
volumes:
- config:/home/container/config
- database:/home/container/database
- plugins:/home/container/plugins
restart: unless-stopped
container_name: open-ticket
volumes:
config:
database:
plugins:
+671
View File
@@ -0,0 +1,671 @@
/// <reference types="node"/>
import fjs from "formatted-json-stringify"
import fs from "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.PropertyFormatter("blacklistAdd"),
new fjs.PropertyFormatter("blacklistRemove"),
]),
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.PropertyFormatter("transcripts"),
]),
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.PropertyFormatter("topicSetLog"),
new fjs.PropertyFormatter("topicSetDm"),
]),
]),
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.PropertyFormatter("noHistory"),
new fjs.PropertyFormatter("historyNotSupported"),
]),
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("channelCategory"),
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("channelCategory"),
new fjs.PropertyFormatter("channelRenameSource"),
new fjs.PropertyFormatter("busy"),
new fjs.PropertyFormatter("closeBeforeMessage"),
new fjs.PropertyFormatter("closeBeforeAdminMessage"),
new fjs.PropertyFormatter("unableToCreateTicket"),
new fjs.PropertyFormatter("messageMissing"),
new fjs.PropertyFormatter("stateExpired"),
new fjs.PropertyFormatter("panelStateExpired"),
]),
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("originalCategory"),
new fjs.PropertyFormatter("newCategory"),
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("accept"),
new fjs.PropertyFormatter("cancel"),
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("ticketOtherUser"),
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.PropertyFormatter("transcripts"),
new fjs.PropertyFormatter("transcriptsUser"),
]),
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.PropertyFormatter("selectPriorityLevel"),
]),
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(".tools/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(".tools/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"] = "v"+JSON.parse(fs.readFileSync("./package.json").toString()).version
const finalText = formatter.stringify(original)
fs.writeFileSync("./languages/"+language,finalText)
}
+21
View File
@@ -0,0 +1,21 @@
# Docker File for Open Ticket
FROM node:22-alpine
# /home/container keeps Pterodactyl panel compatibility
# chown the workdir so node user can create/remove subdirectories
RUN mkdir -p /home/container && chown node:node /home/container
WORKDIR /home/container
# Install dependencies
COPY --chown=node:node package*.json ./
RUN npm install
# Copy app source
COPY --chown=node:node . .
ENV NODE_ENV=production
# Run as the built-in non-root node user
USER node
CMD ["node", "index.js"]
+42 -3
View File
@@ -1,7 +1,17 @@
<!-- omit from toc -->
# Open Ticket (License)
© 2021 - 2026 - 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
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Copyright (C) 2026 DJdj Development. <https://www.dj-dj.be/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -70,7 +80,7 @@ modification follow.
TERMS AND CONDITIONS
0. Definitions.
1. Definitions.
"This License" refers to version 3 of the GNU General Public License.
@@ -671,4 +681,33 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.
```
### 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 (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 (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.
These additional terms are an extension to the GNU GPL v3 and must be followed alongside the original license terms. In case of any conflict between these additional terms and the GNU GPL v3, the additional terms shall take precedence.
+169 -90
View File
@@ -1,110 +1,189 @@
# Coming Soon: OPEN TICKET v4.0 Beta 🎉
Open Ticket v4 beta will be released **in a few weeks!** So stay tuned!
<p align="center">
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket" width="650px"><br>
<span align="center"><b>The most advanced self-hosted ticket bot for Discord</b></span><br>
<sub align="center">Related Projects:</sub><br>
<a href="https://odplugins.dj-dj.be"><img src="https://apis.dj-dj.be/cdn/opendiscord/logo.png" alt="Open Discord" height="55px"></a><br><br>
<a href="https://discord.com/invite/26vT9wt3n3"><img alt="Discord Invite Link" src="https://img.shields.io/badge/discord-support%20server-5865F2.svg?style=flat-square&logo=discord"></img></a>
<a href="https://github.com/open-discord-bots/open-ticket/releases/tag/v4.2.0"><img alt="Open Ticket Version" src="https://img.shields.io/badge/version-4.2.0-brightgreen.svg?style=flat-square"></img></a>
<a href="https://otdocs.dj-dj.be"><img alt="Open Ticket Documentation" src="https://img.shields.io/badge/discord.js-v14-CB3837.svg?style=flat-square&logo=npm"></img></a>
<a href="https://github.com/open-discord-bots/open-ticket/blob/main/LICENSE"><img alt="Open Ticket License" src="https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square"></img></a>
<a href="https://otdocs.dj-dj.be"><img alt="Open Ticket Stars" src="https://img.shields.io/github/stars/djj123dj/open-ticket?color=yellow&label=stars&logo=github&style=flat-square"></img></a>
<a href="https://github.com/sponsors/DJj123dj"><img alt="Sponsor DJj123dj" src="https://img.shields.io/badge/sponsor-DJj123dj-ea4aaa?style=flat-square&logo=githubsponsors"></img></a>
<a href="https://hub.docker.com/repository/docker/djj123dj/open-ticket"><img alt="Open Ticket supports Docker!" src="https://img.shields.io/badge/docker-supported-2496ED?style=flat-square&logo=docker"></img></a>
<a href=".github/pterodactyl-eggs/README.md"><img alt="Open Ticket supports Pterodactyl Eggs!" src="https://img.shields.io/badge/pterodactyl-supported-10539F?style=flat-square&logo=pterodactyl"></img></a>
</p>
What is Open Ticket v4? Well, it's a complete rewrite of Open Ticket with the main focus being "plugins".
There will be massive stability & speed improvements! Hundreds of new features! And obviously very powerful plugins!
Plugins will be 100x better and advanced than the current system!
<hr>
<p align="center">
Open Ticket is the most <b>advanced and customizable</b> Discord ticket bot available right now. It features more than <b>350+ configurable settings</b> to control almost every aspect of your ticket system.
From <code>HTML transcripts</code> and <code>Advanced Plugins</code> to <code>Claiming & Pinning</code>, <code>Modal Questions & Limits</code>, <code>Detailed Statistics</code>, and much more.
The bot is fully translated into <b>38+ languages</b> and has been battle-tested in large Discord servers. Need help or want to get involved? Feel free to join our <a href="https://discord.dj-dj.be"><b>Discord server</b></a>.
</p>
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket" width="600px">
<h3 align="center"><b>⭐️ Support Open Ticket’s growth by starring this repo! ⭐️</b></h3>
<p align="center"><sup>❤️ Love Open Ticket? <a href="https://github.com/sponsors/DJj123dj">Sponsorships</a> help fuel our HTML transcript servers and future features! ❤️</sup><br>
<img align="center" src=".github/SPONSORS.svg" alt="Open Ticket" width="800px">
</p>
[![discord](https://img.shields.io/badge/discord-join%20our%20server-5865F2.svg?style=flat-square&logo=discord)](https://discord.com/invite/26vT9wt3n3) [![version](https://img.shields.io/badge/version-3.5.7-brightgreen.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/releases/tag/v3.5.7) [![discord.js](https://img.shields.io/badge/discord.js-v14-CB3837.svg?style=flat-square&logo=npm)]() [![license](https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/blob/main/LICENSE) [![stars](https://img.shields.io/github/stars/djj123dj/open-ticket?color=yellow&label=stars&logo=github&style=flat-square)](https://otdocs.dj-dj.be)
---
> **[-> Navigate to (⏱️ Quick Setup)](#️-quick-start)**
> **[-> Navigate to (📚 Documentation)](https://otdocs.dj-dj.be)**
> **[-> Navigate to (📞 Support Server)](https://discord.dj-dj.be)**
> **[-> Navigate to (🧩 Plugins/Addons)](https://odplugins.dj-dj.be)**
> **[-> Navigate to (🦇 Pterodactyl Eggs)](.github/pterodactyl-eggs/README.md)**
### Open Ticket
Open Ticket is the most customisable discord ticket bot that you will ever find on Github! There are more than 150+ options to configure! This includes html transcripts, claiming, unlimited tickets, custom embeds & more! Did you know that even the transcripts are highly customisable? Don't wait and check it out! If you're having trouble setting the bot up, feel free to join our support server and we will help you further!
### 📌 Features
#### Core Features
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-management.svg"></img> **Ticket Management** - Close, reopen, delete, claim or pin tickets with ease.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-commands.svg"></img> **Powerful Commands** - Manage your support system with **30+ commands** for staff & users.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-questions.svg"></img> **Modal Questions** - Ask users **custom questions** before a ticket is created.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-priorities.svg"></img> **Priorities** - Assign **priority levels** to tickets to highlight urgent requests.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-participants.svg"></img> **Participants** - Add or remove participants & transfer ownership from one user to another.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-adjustments.svg"></img> **Adjustments** - Rename tickets, change ticket types or transfer ownership.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-blacklist.svg"></img> **Blacklist & Limits** - Prevent users from creating tickets and set per-user or global ticket limits.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-customisable.svg"></img> **Highly Customisable** - Configure **350+ settings** covering appearance and behaviour.
Take a look at all the features and discover the possibilities!
### [📚 Read The Documentation!](https://otdocs.dj-dj.be/)
### [🎥 Or Watch The YouTube Tutorial!](https://www.youtube.com/watch?v=2jK9kAf6ASU)
[![Open Ticket Full-Setup Guide](https://img.youtube.com/vi/2jK9kAf6ASU/0.jpg)](https://www.youtube.com/watch?v=2jK9kAf6ASU)
#### Ticket Automation & Workflows
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-unlimited.svg"></img> **Unlimited Possibilities** - Create unlimited tickets, panels & question flows.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-autoclose.svg"></img> **Autoclose Tickets** - Automatically **close tickets** after predefined conditions.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-autodelete.svg"></img> **Autodelete Tickets** - Automatically **delete closed tickets** to keep channels clean.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/automation-categories.svg"></img> **Category Routing** - Move tickets between categories based on claim or close state.
## 📌 Features
- **🎉 html transcripts** - Make use of the most customisable, beautiful and easy-to-use HTML Transcripts!
- **✅ ticket actions** - Close, Re-Open, Delete, Rename & Move all your tickets!
- **🇬🇧 translation** - Open Ticket has been translated in more than **33 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 everywhere** - Create an infinite amount of tickets & panels!
- **📝 custom plugins** - Create the most advanced plugins that you want or use premade ones by our community!
- **👥 user management** - Add & Remove users from all tickets!
- **📊 detailed stats** - Open Ticket has ticket, user & global staticstics available for everyone!
- **📦 commands** Open Ticket supports both Slash Commands & Text Commands!
- **📥 extra types** - The bot also supports Reaction Roles & Url Buttons, because why not ¯\\_(ツ)_/¯
- **🎛️ dependencies** - We try to use as little external dependencies as needed!
#### Transcripts & Insights
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/insights-transcripts.svg"></img> **HTML Transcripts** - Generate beautiful, easy-to-read **HTML transcripts** for every ticket.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/insights-stats.svg"></img> **Detailed Statistics** - Track **50+ statistics** for tickets, users and server activity.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/insights-logs.svg"></img> **Ticket Logs** - Track **all ticket events** such as creation, closures, and staff actions.
## 📸 Preview
<img src="https://apis.dj-dj.be/cdn/openticket/message_buttons.png" alt="Message with buttons" width="350px">
<img src="https://apis.dj-dj.be/cdn/openticket/message_dropdown.png" alt="Message with dropdown" width="350px">
<br>
<img src="https://apis.dj-dj.be/cdn/openticket/firstmsg.png" alt="Ticket start message" width="350px">
<img src="https://apis.dj-dj.be/cdn/openticket/command_close.png" alt="Ticket start message" width="350px">
#### User Experience
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-translated.svg"></img> **Fully Translated** - Available in **38+ languages**, translated and maintained by the community.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-interactions.svg"></img> **Modern Interactions** - Full support for buttons, dropdowns, slash/text commands & modals.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-panels.svg"></img> **Panels** - Create messages with buttons or a dropdown for users to open tickets.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ux-sub-panels.svg"></img> **Sub-Panels** - One panel not enough? Use multiple panels to offer more choices.
## ❤️ Sponsors
A big thanks to all our sponsors! Without them, it wouldn't be possible to create this project!
#### Plugins & Ecosystem
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-plugins.svg"></img> **Plugin System** - Use custom plugins to **add new features** or **modify existing behavior** of the bot.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-community.svg"></img> **Community Plugins** - Use and share plugins built by the community.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-api.svg"></img> **Advanced API** - Build advanced plugins with access to ticket events and internal systems.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-integrations.svg"></img> **Integrations** - Connect Open Ticket with external services to automate workflows across platforms.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/ecosystem-bonus.svg"></img> **Bonus Features** - Somehow, we included Reaction Roles and URL Button support as well.
|<img src="https://github.com/SpyEye2.png" alt="Profile Picture" width="80px"></img>|<img src="https://github.com/DOSEV5.png" alt="Profile Picture" width="80px"></img>|<img src="https://github.com/mods-hd.png" alt="Profile Picture" width="80px"></img>|
|----|-|-|
|**[SpyEye](https://github.com/SpyEye2)**|**[DOSEV5](https://github.com/DOSEV5)**|**[Mods HD](https://github.com/mods-hd)**|
#### Deployment
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-quick.svg"></img> **Quick Setup** - Easy **5-minute configuration** using the Interactive Setup CLI.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-scale.svg"></img> **Scalable & Reliable** - Battle-tested in servers with **100k+ members**.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-secure.svg"></img> **Private & Secure** - Used by thousands of servers with respect for security & privacy.
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-pterodactyl.svg"></img> **Pterodactyl Support** - 100% compatible with Pterodactyl panels. [(Download official eggs)](.eggs/README.md)
- <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/deployment-docker.svg"></img> **Docker Support** - Deploy Open Ticket in minutes with Docker containers.
*Do you also want to be in this list? Don't wait & sponsor us now! 😄*
#### Extend functionality even more with our [pre-made community plugins](#-plugins)!
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-reviews.svg"></img> **Reviews** - Create and manage a support review system for tickets.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-tags.svg"></img> **Tags** - Define keywords that automatically trigger predefined responses.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-reminders.svg"></img> **Reminders** - Create and manage custom reminders for users or staff.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-ai.svg"></img> **AI Integrations** - Connect to AI providers such as ChatGPT, Claude, or Gemini.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-display.svg"></img> **Channel Display** - Create voice channels that display real-time ticket system statistics.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-forms.svg"></img> **Forms** - Build advanced forms for collecting structured information from users.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-embeds.svg"></img> **Custom Embeds** - Create and send custom embeds via commands.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/core-customisable.svg"></img> **Customization Tools** - Additional configuration options for advanced behavior and styling.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-dashboard.svg"></img> **Web Dashboard** - Configure and manage the bot through a remote web dashboard.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-feedback.svg"></img> **Feedback** - Collect user feedback after ticket deletion using forms.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-sqlite.svg"></img> **SQLite Database** - Use an SQLite backend for improved performance and lightweight storage.
> - <img align="center" src="https://apis.dj-dj.be/cdn/openticket/readme-icons/plugin-more.svg"></img> **And more** - Additional community plugins are available and actively expanding.
## 🛠️ Contributors
### Official Team
|Role |User (discord name)|
|-------------------|-------------------|
|🖥️ Lead Developer |djj123dj |
|👥 Support Team |sankedev |
## ⏱️ Quick Start
> 1. Download the [latest version of Open Ticket](https://github.com/open-discord-bots/open-ticket/releases/latest).
> 2. Make sure you have installed Node.js on your system (check using `node -v`, minimum `v20`).
> 3. Install any required dependencies using `npm install`.
> 4. Configure the bot in one of the following ways:
> - Method 1 (Easy): Start the **Quick Setup CLI** using `npm run setup`.
> - Method 2 (Hard): Manual **JSON configuration** in `./config/...`
> 5. If using the **Quick Setup CLI**, click on `> ⏱️ Quick Setup` and follow the instructions.
> 6. Start the bot using `npm start` or `node index.js`
> - If any config errors occur, the bot will give you a report of how to solve them.
> - Follow the instructions and restart the bot.
> 7. Enjoy using Open Ticket!
> 8. Install plugins from the [**Official Plugin Repository**](https://github.com/open-discord-bots/plugins)
>
> #### 🚦 Next Steps
> **[-> Navigate to (📚 Documentation)](https://otdocs.dj-dj.be)**
> **[-> Navigate to (📞 Support Server)](https://discord.dj-dj.be)**
> **[-> Navigate to (🧩 Plugins/Addons)](https://odplugins.dj-dj.be)**
> **[-> Navigate to (🦇 Pterodactyl Eggs)](.github/pterodactyl-eggs/README.md)**
>
> #### 🖥️ Recommended Hosting
> - **A VPS (Virtual Private Server)** - Extra customisation & more stability. Recommended for most servers.
> - **Any Pterodactyl-Based Panel** - Easy installation & configuration.
### Translators
|Language |Maintainer (discord name) |Status |
|---------------------|--------------------------|---------------|
|🇬🇧 English |djj123dj |🟢 Up To Date |
|🇳🇱 Dutch |djj123dj |🟢 Up To Date |
|🇫🇷 French |sankedev & tostam |🟢 Up To Date |
|🇷🇴 Romanian |sankedev |🟢 Up To Date |
|🇪🇸 Spanish |redactado & josuens |🟢 Up To Date |
|🇩🇪 German |david.3 |🟢 Up To Date |
|🇮🇹 Italian |maurizio26 |🟢 Up To Date |
|🇨🇿 Czech |spyeye_ |🟢 Up To Date |
|🇦🇪 Arabic |deqressing |🟢 Up To Date |
|🇩🇰 Danish |.the_gamer |🟢 Up To Date |
|🇵🇹 Portuguese |quiradon |🔴 Outdated |
|🇷🇺 Russian |apexo & ander |🟢 Up To Date |
|🇺🇦 Ukrainian |ander |🟢 Up To Date |
|🇹🇷 Turkish |Hydra#0009 |🔴 Outdated |
|🇵🇱 Polish |mkevas |🟢 Up To Date |
|🇸🇮 Slovenian |n1kkec |🔴 Outdated |
|🇹🇭 Thai |modshd |🟢 Up To Date |
|🇳🇴 Norwegian |noonenook |🟢 Up To Date |
|🇬🇷 Greek |stefanos__. |🔴 Outdated |
|🇮🇩 Indonesian |erxg |🟢 Up To Date |
|❓ Kurdish |raze.hama |🟢 Up To Date |
|🇭🇺 Hungarian |Snowy |🔴 Outdated |
|❓ Persian |sasanwm |🟢 Up To Date |
|🇱🇻 Latvian |ronalds1398 |🟢 Up To Date |
|🇪🇪 Estonian |iamnotmega |🟢 Up To Date |
|🇯🇵 Japanese |iamnotmega |🟢 Up To Date |
|🇰🇷 Korean |iamnotmega |🟢 Up To Date |
|🇨🇳 Simplified Chinese |iamnotmega |🟢 Up To Date |
|🇨🇳 Traditional Chinese|iamnotmega |🟢 Up To Date |
|🇫🇮 Finnish |iamnotmega |🟢 Up To Date |
|🇸🇪 Swedish |iamnotmega |🟢 Up To Date |
|🇻🇳 Vietnamese |iamnotmega |🟢 Up To Date |
## 📸 Previews
<img alt="Preview of: Advanced Ticket Management" src="https://apis.dj-dj.be/cdn/openticket/ot-previews/preview-1.png">
<img alt="Preview of: Customisable Ticket Panels" src="https://apis.dj-dj.be/cdn/openticket/ot-previews/preview-2.png">
<img alt="Preview of: Modal Questions & Forms" src="https://apis.dj-dj.be/cdn/openticket/ot-previews/preview-3.png">
<img alt="Preview of: Detailed Statistics & Insights" src="https://apis.dj-dj.be/cdn/openticket/ot-previews/preview-4.png">
<img alt="Preview of: 30+ Powerful Commands" src="https://apis.dj-dj.be/cdn/openticket/ot-previews/preview-5.png">
<img alt="Preview of: Buttons, Transcripts, Limits & More!" src="https://apis.dj-dj.be/cdn/openticket/ot-previews/preview-6.png">
## 💬 Translations
With the amazing support of our translators, we've been able to translate Open Ticket in more than **38 languages**!
#### Categories: 🟢 Available - 🤖 Partially Made Using AI - 🟠 Incomplete - 🔴 Unavailable/Outdated
|🔍 |Languages (38) |Config Value |Maintainers (Github/Discord) |
|----|----------------------|------------------------|--------------------------------|
|🟢 |🇬🇧 English |`"english"` |djj123dj |
|🟢 |🇳🇱 Dutch |`"dutch"` |djj123dj |
|🟢 |🇩🇪 German |`"german"` |benzorich |
|🟢 |🇫🇷 French |`"french"` |guillee.3 |
|🟢 |🇪🇸 Spanish |`"spanish"` |Reddishye & josuens |
|🟢 |🇵🇹 Portuguese |`"portuguese"` |quiradon |
|🟢 |🇮🇹 Italian |`"italian"` |fraden1mvp. & imperatorix_17 |
|🟢 |🇸🇪 Swedish |`"swedish"` |NoOneNook |
|🟢 |🇳🇴 Norwegian |`"norwegian"` |NoOneNook |
|🟢 |🇹🇭 Thai |`"thai"` |modshd |
|🟢 |🇮🇳 Hindi |`"hindi"` |challenger_nova |
|🟢 |🇭🇺 Hungarian |`"hungarian"` |kornel0706 |
|🟢 |🇮🇩 Indonesian |`"indonesian"` |erxg |
|🟢 |🇱🇹 Lithuanian |`"lithuanian"` |tsgindrius |
|🟢 |🇺🇦 Ukrainian |`"ukrainian"` |anderskiy |
|🟢 |🇨🇿 Czech |`"czech"` |spyeye_ |
|🟢 |🇷🇴 Romanian |`"romanian"` |sankedev |
|🟢 |🇩🇰 Danish |`"danish"` |the_gamer |
|🟢 |🇹🇷 Turkish |`"turkish"` |palestinian |
|🟢 |🇦🇪 Arabic |`"arabic"` |palestinian |
|🟢 |🇵🇱 Polish |`"polish"` |danoglez |
|🟢 |🇮🇷 Persian |`"persian"` |dysashop & zhavis |
|🟢 |🇧🇩 Bengali |`"bengali"` |HanumeshGupta |
|🟢 |❓ Catalan |`"catalan"` |guillee3 |
|🟢 |🇨🇳 Traditional Chinese|`"traditional-chinese"` |me.october |
|🟢 |🇰🇭 Khmer (Cambodia) |`"khmer"` |yuuslokrobjakkroval |
|🤖 |🇪🇪 Estonian |`"estonian"` |iamnotmega |
|🤖 |🇫🇮 Finnish |`"finnish"` |iamnotmega |
|🤖 |🇯🇵 Japanese |`"japanese"` |HanumeshGupta |
|🤖 |🇬🇷 Greek |`"greek"` |HanumeshGupta |
|🤖 |🇸🇮 Slovenian |`"slovenian"` |HanumeshGupta |
|🤖 |🇰🇷 Korean |`"korean"` |HanumeshGupta |
|🤖 |🇮🇳 Tamil |`"tamil"` |HanumeshGupta |
|🤖 |❓ Kurdish |`"kurdish"` |HanumeshGupta |
|🤖 |🇷🇺 Russian |`"russian"` |NoOneNook |
|🤖 |🇱🇻 Latvian |`"latvian"` |NoOneNook |
|🤖 |🇻🇳 Vietnamese |`"vietnamese"` |ngocdiep2006 |
|🤖 |🇨🇳 Simplified Chinese |`"simplified-chinese"` |HanumeshGupta |
<!--[⭐ Contribute!](.github/CONTRIBUTING.md) -->
## 😎 Hall Of Fame
<img alt="The full list of contributors for Open Ticket and Open Discord." src=".github/CONTRIBUTORS.svg">
## ⭐️ Star History
Please help us grow by giving a star! It would help us a lot!
If you enjoy using Open ticket, **consider starring** our repository.
This will help us grow and reach even more people!
<a href="https://star-history.com/#DJj123dj/open-ticket&Date">
<a href="https://star-history.com/#open-discord-bots/open-ticket&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=DJj123dj/open-ticket&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=DJj123dj/open-ticket&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=DJj123dj/open-ticket&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=open-discord-bots/open-ticket&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=open-discord-bots/open-ticket&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=open-discord-bots/open-ticket&type=Date" />
</picture>
</a>
## 📎 Links
current version: _v3.5.7_
</br>Changelog: [click here](https://otgithub.dj-dj.be/releases)
</br>Documentation: [click here](https://otdocs.dj-dj.be/)
---
<img src="https://apis.dj-dj.be/cdn/openticket/logo.png" alt="Open Ticket Logo" width="170px">
© 2024 - DJdj Development | [website](https://www.dj-dj.be) | [discord](https://discord.dj-dj.be) | [terms of service](https://www.dj-dj.be/terms#terms) | [privacy policy](https://www.dj-dj.be/terms#privacy)
**README.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>
© 2021 - 2026 - [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)
-92
View File
@@ -1,92 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded add.js")
if (!DISABLE.commands.text.add) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"add")) return
var user = msg.mentions.users.first()
if (!user) return msg.channel.send({embeds:[bot.errorLog.invalidArgsMessage(l.errors.missingArgsDescription+" `<user>`:\n`"+config.prefix+"add <user>`")]})
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
/**@type {discord.PermissionOverwriteManager} */
const overwrites = msg.channel.permissionOverwrites
overwrites.create(user.id,{
ViewChannel:true,
AddReactions:true,
AttachFiles:true,
EmbedLinks:true,
SendMessages:true,
ReadMessageHistory:true
},{reason:"Added user to ticket."})
msg.channel.send({embeds:[bot.embeds.commands.addEmbed(user,msg.author)]})
var loguser = msg.mentions.users.first()
log("command","someone used the 'add' command",[{key:"user",value:msg.author.username}])
log("system","user added to ticket",[{key:"user",value:msg.author.username},{key:"ticket",value:msg.channel.name},{key:"added_user",value:loguser.username}])
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
APIEvents.onTicketAdd(msg.author,loguser,msg.channel,msg.guild,new Date(),{status:"open",name:msg.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("add",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.add) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "add") return
const user = interaction.options.getUser("user")
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return
}
await interaction.deferReply()
/**@type {discord.PermissionOverwriteManager} */
const overwrites = interaction.channel.permissionOverwrites
overwrites.create(user.id,{
ViewChannel:true,
AddReactions:true,
AttachFiles:true,
EmbedLinks:true,
SendMessages:true,
ReadMessageHistory:true
},{reason:"Added user to ticket."})
interaction.editReply({embeds:[bot.embeds.commands.addEmbed(user,interaction.user)]})
var loguser = user
log("command","someone used the 'add' command",[{key:"user",value:interaction.user.username}])
log("system","user added to ticket",[{key:"user",value:interaction.user.username},{key:"ticket",value:interaction.channel.name},{key:"added_user",value:loguser.username}])
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
APIEvents.onTicketAdd(interaction.user,loguser,interaction.channel,interaction.guild,new Date(),{status:"open",name:interaction.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("add",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-134
View File
@@ -1,134 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded change.js")
if (!DISABLE.commands.text.change) client.on("messageCreate", async msg => {
if (!msg.content.startsWith(config.prefix+"change")) return
if (!msg.channel.isTextBased()) return
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
const list = []
config.options.forEach((o) => {
if (o.type == "ticket") list.push(o.id)
})
var newtype = msg.content.split(config.prefix+"change")[1].substring(1)
if (!newtype) return msg.channel.send({embeds:[bot.errorLog.invalidIdChooseFromList(list,l.errors.missingArgsDescription+" `<type>`:\n`"+config.prefix+"change <type>`")]})
const newTicket = require("../core/utils/configParser").getTicketById(newtype,true)
if (!newTicket) return msg.channel.send({embeds:[bot.errorLog.invalidIdChooseFromList(list,l.errors.missingArgsDescription+" `<type>`:\n`"+config.prefix+"change <type>`")]})
/**@type {discord.PermissionOverwriteManager} */
const prePermsManager = msg.channel.permissionOverwrites
const prePerms = prePermsManager.cache
/**@type {String} */
var chName = msg.channel.name
const splitted = chName.split("-")
const prefix = splitted.shift()
const name = (splitted.length > 0) ? splitted.join("-") : prefix
if (newTicket.category){
const parent = await msg.guild.channels.fetch(newTicket.category,{cache:true})
if (parent && parent.type == discord.ChannelType.GuildCategory) msg.channel.setParent(parent)
}
prePermsManager.set(prePerms)
msg.channel.setName(newTicket.channelprefix+name).catch(() => {
msg.channel.send({embeds:[bot.errorLog.failedRenameChannel]})
})
hiddendata.find((value,index) => {
if (value.key == "type"){
hiddendata[index] = {key:"type",value:newtype}
}
})
msg.channel.send({embeds:[bot.embeds.commands.changeEmbed(msg.author,newtype)]})
log("command","someone used the 'change' command",[{key:"user",value:msg.author.username}])
log("system","ticket type changed",[{key:"user",value:msg.author.username},{key:"ticket",value:name},{key:"newtype",value:newtype}])
APIEvents.onCommand("change",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.change) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "change") return
if (!interaction.channel.isTextBased()) return
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return
}
await interaction.deferReply()
const list = []
config.options.forEach((o) => {
if (o.type == "ticket") list.push(o.id)
})
var newtype = interaction.options.getString("type",true)
const newTicket = require("../core/utils/configParser").getTicketById(newtype,true)
if (!newTicket) return interaction.editReply({embeds:[bot.errorLog.invalidIdChooseFromList(list,l.errors.missingArgsDescription+" `<type>`:\n`"+config.prefix+"change <type>`")]})
/**@type {discord.PermissionOverwriteManager} */
const prePermsManager = interaction.channel.permissionOverwrites
const prePerms = prePermsManager.cache
/**@type {String} */
var chName = interaction.channel.name
const splitted = chName.split("-")
const prefix = splitted.shift()
const name = (splitted.length > 0) ? splitted.join("-") : prefix
if (newTicket.category){
const parent = await interaction.guild.channels.fetch(newTicket.category,{cache:true})
if (parent && parent.type == discord.ChannelType.GuildCategory) interaction.channel.setParent(parent)
}
prePermsManager.set(prePerms)
interaction.channel.setName(newTicket.channelprefix+name).catch(() => {
interaction.channel.send({embeds:[bot.errorLog.failedRenameChannel]})
})
hiddendata.find((value,index) => {
if (value.key == "type"){
hiddendata[index] = {key:"type",value:newtype}
}
})
interaction.editReply({embeds:[bot.embeds.commands.changeEmbed(interaction.user,newtype)]})
log("command","someone used the 'change' command",[{key:"user",value:interaction.user.username}])
log("system","ticket type changed",[{key:"user",value:interaction.user.username},{key:"ticket",value:name},{key:"newtype",value:newtype}])
APIEvents.onCommand("change",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-107
View File
@@ -1,107 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const embed = discord.EmbedBuilder
const storage = bot.storage
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded claim.js")
if (!DISABLE.commands.text.claim) client.on("messageCreate", async msg => {
if (!msg.content.startsWith(config.prefix+"claim")) return
var user = msg.mentions.users.first()
const claimingUser = user ? user : msg.author
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
hiddendata.push({key:"claimedby",value:claimingUser.id})
bot.hiddenData.writeHiddenData(msg.channel.id,hiddendata)
storage.set("claimData",msg.channel.id,claimingUser.id)
await msg.channel.messages.fetchPinned().then(msglist => {
var firstmsg = msglist.last()
if (firstmsg == undefined || firstmsg.author.id != client.user.id) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const newEmbed = new embed(firstmsg.embeds[0].data)
.setFooter({text:"claimed by: "+msg.author.username,iconURL:msg.author.displayAvatarURL()})
if (firstmsg.components[0].components[1] && firstmsg.components[0].components[1].disabled){
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabledNoClaim],embeds:[newEmbed]})
}else{
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowNormalNoClaim],embeds:[newEmbed]})
}
})
msg.channel.send({embeds:[bot.embeds.commands.claimEmbed(claimingUser,msg.author)]})
log("command","someone used the 'claim' command",[{key:"user",value:msg.author.username}])
log("system","user claimed to ticket",[{key:"user",value:msg.author.username},{key:"ticket",value:msg.channel.name},{key:"claimed_user",value:claimingUser.username}])
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
APIEvents.onTicketClaim(msg.author,loguser,msg.channel,msg.guild,new Date(),{status:"open",name:msg.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("claim",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.claim) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "claim") return
const user = interaction.options.getUser("user",false) ? interaction.options.getUser("user",true) : interaction.user
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return interaction.reply({content:":x: "+l.errors.noPermsTitle,ephemeral:true})
}
await interaction.deferReply()
hiddendata.push({key:"claimedby",value:user.id})
bot.hiddenData.writeHiddenData(interaction.channel.id,hiddendata)
storage.set("claimData",interaction.channel.id,user.id)
await interaction.channel.messages.fetchPinned().then(msglist => {
var firstmsg = msglist.last()
if (firstmsg == undefined || firstmsg.author.id != client.user.id) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const newEmbed = new embed(firstmsg.embeds[0].data)
.setFooter({text:"claimed by: "+user.username,iconURL:user.displayAvatarURL()})
if (firstmsg.components[0].components[1] && firstmsg.components[0].components[1].disabled){
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabledNoClaim],embeds:[newEmbed]})
}else{
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowNormalNoClaim],embeds:[newEmbed]})
}
})
interaction.editReply({embeds:[bot.embeds.commands.claimEmbed(user,interaction.user)]})
log("command","someone used the 'claim' command",[{key:"user",value:interaction.user.username}])
log("system","user claimed to ticket",[{key:"user",value:interaction.user.username},{key:"ticket",value:interaction.channel.name},{key:"claimed_user",value:user.username}])
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
APIEvents.onTicketClaim(interaction.user,user,interaction.channel,interaction.guild,new Date(),{status:"open",name:interaction.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("claim",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-85
View File
@@ -1,85 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded close.js")
if (!DISABLE.commands.text.close) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"close")) return
const args = msg.content.substring((config.prefix+"close").length).split(" ")
args.shift()
/**@type {String|false} */
var tempreason = args.join(" ")
if (!tempreason || tempreason == " " || tempreason == " "){
tempreason = false
}
const reason = tempreason
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (hiddendata.find(h => h.key == "pendingdelete")) return msg.channel.send({embeds:[bot.errorLog.warning("Warning!","You can't close a ticket while it's being deleted!")]})
const descriptionReason = reason ? "**"+l.messages.reason+":** "+reason : false
msg.channel.send({embeds:[bot.embeds.commands.closeEmbed(msg.author,descriptionReason)],components:[bot.buttons.close.closeCommandRow]})
var name = msg.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
require("../core/ticketActions/ticketCloser").closeManager(msg.member,msg.channel,prefix,"close",reason,true)
log("command","someone used the 'close' command",[{key:"user",value:msg.author.username}])
APIEvents.onCommand("close",true,msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.close) client.on("interactionCreate",async(interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "close") return
//interaction.deferReply()
const reason = interaction.options.getString("reason") ? interaction.options.getString("reason") : false
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (hiddendata.find(h => h.key == "pendingdelete")) return interaction.reply({embeds:[bot.errorLog.warning("Warning!","You can't close a ticket while it's being deleted!")]})
await interaction.deferReply()
const descriptionReason = reason ? "**"+l.messages.reason+":** "+reason : false
interaction.editReply({embeds:[bot.embeds.commands.closeEmbed(interaction.user,descriptionReason)],components:[bot.buttons.close.closeCommandRow]})
var name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
require("../core/ticketActions/ticketCloser").closeManager(interaction.member,interaction.channel,prefix,"close",false,true)
log("command","someone used the 'close' command",[{key:"user",value:interaction.user.username}])
APIEvents.onCommand("close",true,interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-80
View File
@@ -1,80 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded delete.js")
if (!DISABLE.commands.text.delete) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"delete")) return
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
msg.channel.send({embeds:[bot.embeds.commands.deleteEmbed(msg.author)]})
var name = msg.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
require("../core/ticketActions/ticketCloser").closeManager(msg.member,msg.channel,prefix,"delete",false,true)
log("command","someone used the 'delete' command",[{key:"user",value:msg.author.username}])
APIEvents.onCommand("delete",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.delete) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "delete") return
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return interaction.reply({embeds:[bot.errorLog.noPermsDelete(interaction.user)]})
}
await interaction.deferReply()
interaction.editReply({embeds:[bot.embeds.commands.deleteEmbed(interaction.user)]})
var name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
require("../core/ticketActions/ticketCloser").closeManager(interaction.member,interaction.channel,prefix,"delete",false,true)
log("command","someone used the 'delete' command",[{key:"user",value:interaction.user.username}])
APIEvents.onCommand("delete",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-58
View File
@@ -1,58 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded help.js")
const msgName = config.system.showSlashcmdsInHelp ? "message" : "msg"
const prefix = config.system.showSlashcmdsInHelp ? "/" : config.prefix
const helpEmbed = new discord.EmbedBuilder()
.setColor(config.color)
.setTitle("❔ "+l.helpMenu.title)
const header = l.helpMenu.header2
helpEmbed.setDescription(header+"`"+prefix+msgName+" <id>` ➜ _"+l.helpMenu.msgCmd+"_\n\n`"+prefix+"rename <name>` ➜ _"+l.helpMenu.renameCmd+"_\n`"+prefix+"close [reason]` ➜ _"+l.helpMenu.closeCmd+"_\n`"+prefix+"delete` ➜ _"+l.helpMenu.deleteCmd+"_\n`"+prefix+"reopen` ➜ _"+l.helpMenu.reopenCmd+"_\n\n`"+prefix+"add <user>` ➜ _"+l.helpMenu.addCmd+"_\n`"+prefix+"remove <user>` ➜ _"+l.helpMenu.removeCmd+"_\n\n`"+prefix+"change <newtype>` ➜ _"+l.helpMenu.changeCmd+"_\n`"+prefix+"claim [user]` ➜ _"+l.helpMenu.claimCmd+"_\n`"+prefix+"unclaim` ➜ _"+l.helpMenu.unclaimCmd+"_")
helpEmbed.addFields([
{name:"Autoclose",value:l.helpMenu.autocloseCmd+"\n`"+prefix+"autoclose enable <inactive time>`\n`"+prefix+"autoclose disable`"},
{name:"Stats",value:"Get statistics from the bot"+"\n`"+prefix+"stats global`\n`"+prefix+"stats ticket`\n`"+prefix+"stats user [user]`"}
])
var otherprefix = prefix.endsWith(" ") ? prefix.substring(0,prefix.length-1) : prefix
if (!DISABLE.commands.text.help) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix)) return
var args = msg.content.split(config.prefix)
if (msg.content == config.prefix || msg.content == config.prefix+" " || msg.content == otherprefix){
msg.channel.send({embeds:[helpEmbed]})
log("command","someone used the 'help' command",[{key:"user",value:msg.author.username}])
APIEvents.onCommand("help",true,msg.author,msg.channel,msg.guild,new Date())
return
}
if (args[1]){
if (!args[1].startsWith("close") && !args[1].startsWith("delete") && !args[1].startsWith("remove") && !args[1].startsWith("add") && !args[1].startsWith("msg") && !args[1].startsWith("remove") && !args[1].startsWith("rename") && !args[1].startsWith("reopen") && !args[1].startsWith("change") && !args[1].startsWith("claim") && !args[1].startsWith("unclaim") && !args[1].startsWith("stats")){
msg.channel.send({embeds:[helpEmbed]})
log("command","someone used the 'help' command",[{key:"user",value:msg.author.username}])
APIEvents.onCommand("help",true,msg.author,msg.channel,msg.guild,new Date())
}
}
})
if (!DISABLE.commands.slash.help) client.on("interactionCreate",(interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "help") return
interaction.reply({embeds:[helpEmbed]})
log("command","someone used the 'help' command",[{key:"user",value:interaction.user.username}])
APIEvents.onCommand("help",true,interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-78
View File
@@ -1,78 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded remove.js")
if (!DISABLE.commands.text.remove) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"remove")) return
var user = msg.mentions.users.first()
if (!user) return msg.channel.send({embeds:[bot.errorLog.invalidArgsMessage(l.errors.missingArgsDescription+" `<user>`:\n`"+config.prefix+"remove <user>`")]})
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
/**@type {discord.PermissionOverwriteManager} */
const overwrites = msg.channel.permissionOverwrites
overwrites.delete(user.id,"Removed user from ticket.")
msg.channel.send({embeds:[bot.embeds.commands.removeEmbed(user,msg.author)]})
var loguser = msg.mentions.users.first()
log("command","someone used the 'remove' command",[{key:"user",value:msg.author.username}])
log("system","user removed from ticket",[{key:"user",value:msg.author.username},{key:"ticket",value:msg.channel.name},{key:"removed_user",value:loguser.username}])
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
APIEvents.onTicketRemove(msg.author,loguser,msg.channel,msg.guild,new Date(),{status:"open",name:msg.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("remove",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.remove) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "remove") return
const user = interaction.options.getUser("user")
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return
}
await interaction.deferReply()
/**@type {discord.PermissionOverwriteManager} */
const overwrites = interaction.channel.permissionOverwrites
overwrites.delete(user.id,"Removed user from ticket.")
interaction.editReply({embeds:[bot.embeds.commands.removeEmbed(user,interaction.user)]})
var loguser = user
log("command","someone used the 'remove' command",[{key:"user",value:interaction.user.username}])
log("system","user removed from ticket",[{key:"user",value:interaction.user.username},{key:"ticket",value:interaction.channel.name},{key:"removed_user",value:loguser.username}])
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
APIEvents.onTicketRemove(interaction.user,loguser,interaction.channel,interaction.guild,new Date(),{status:"open",name:interaction.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("remove",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-88
View File
@@ -1,88 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded rename.js")
if (!DISABLE.commands.text.rename) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"rename")) return
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
var newname = msg.content.split(config.prefix+"rename")[1].substring(1)
if (!newname) return msg.channel.send({embeds:[bot.errorLog.invalidArgsMessage(l.errors.missingArgsDescription+" `<name>`:\n`"+config.prefix+"rename <name>`")]})
var name = msg.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
if (!prefix) prefix = "noprefix-"
msg.channel.setName(prefix+newname)
msg.channel.send({embeds:[bot.embeds.commands.renameEmbed(msg.author,prefix+newname)]})
log("command","someone used the 'rename' command",[{key:"user",value:msg.author.username}])
log("system","ticket renamed",[{key:"user",value:msg.author.username},{key:"ticket",value:name},{key:"newname",value:newname}])
APIEvents.onCommand("rename",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.rename) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "rename") return
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return
}
await interaction.deferReply()
var newname = interaction.options.getString("name")
var name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
if (!prefix) prefix = "noprefix-"
interaction.channel.setName(prefix+newname)
interaction.editReply({embeds:[bot.embeds.commands.renameEmbed(interaction.user,prefix+newname)]})
log("command","someone used the 'rename' command",[{key:"user",value:interaction.user.username}])
log("system","ticket renamed",[{key:"user",value:interaction.user.username},{key:"ticket",value:name},{key:"newname",value:newname}])
APIEvents.onCommand("rename",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-51
View File
@@ -1,51 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded reopen.js")
if (!DISABLE.commands.text.reopen) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"reopen")) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (hiddendata.find(h => h.key == "pendingdelete")) return msg.channel.send({embeds:[bot.errorLog.warning("Warning!","You can't re-open a ticket while it's being deleted!")]})
msg.channel.send({embeds:[bot.embeds.commands.reopenEmbed(msg.author)],components:[bot.buttons.close.openRowNormal]})
require("../core/ticketActions/ticketReopener").reopenTicket(msg.guild,msg.channel,msg.author,ticketId)
log("command","someone used the 'reopen' command",[{key:"user",value:msg.author.username}])
APIEvents.onCommand("reopen",true,msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.reopen) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "reopen") return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (hiddendata.find(h => h.key == "pendingdelete")) return interaction.reply({embeds:[bot.errorLog.warning("Warning!","You can't re-open a ticket while it's being deleted!")]})
await interaction.deferReply()
await interaction.editReply({embeds:[bot.embeds.commands.reopenEmbed(interaction.user)],components:[bot.buttons.close.openRowNormal]})
require("../core/ticketActions/ticketReopener").reopenTicket(interaction.guild,interaction.channel,interaction.user,ticketId)
log("command","someone used the 'reopen' command",[{key:"user",value:interaction.user.username}])
APIEvents.onCommand("reopen",true,interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-103
View File
@@ -1,103 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded stats.js")
if (!DISABLE.commands.text.stats) client.on("messageCreate",async (msg) => {
if (!msg.content.startsWith(config.prefix+"stats global") && !msg.content.startsWith(config.prefix+"stats ticket") && !msg.content.startsWith(config.prefix+"stats user")) return
if (!msg.guild) return
/**@type {"global"|"ticket"|"user"} */
const mode = msg.content.split(config.prefix+"stats ")[1]
if (mode == "global"){
//send global stats
msg.channel.send({embeds:[await bot.statsManager.createGlobalStatsEmbed()]})
}else if (mode == "user"){
//send user stats
const mention = msg.mentions.users.first()
if (mention){
//get user from mention
if (!bot.statsManager.existStats("user","TICKETS_CREATED",mention.id)){
msg.channel.send({embeds:[bot.errorLog.serverError(l.stats.errorUserNotFound)]})
return
}
msg.channel.send({embeds:[await bot.statsManager.createUserStatsEmbed(msg.guild,mention,msg.channel.id)]})
}else{
//use author as user
if (!bot.statsManager.existStats("user","TICKETS_CREATED",msg.author.id)){
msg.channel.send({embeds:[bot.errorLog.serverError(l.stats.errorUserNotFound)]})
return
}
msg.channel.send({embeds:[await bot.statsManager.createUserStatsEmbed(msg.guild,msg.author,msg.channel.id)]})
}
}else if (mode == "ticket"){
//send ticket stats
if (!bot.statsManager.existStats("ticket","STATUS",msg.channel.id)){
msg.channel.send({embeds:[bot.errorLog.notInATicket]})
return
}
msg.channel.send({embeds:[await bot.statsManager.createTicketStatsEmbed(msg.guild,msg.channel.id)]})
}
log("command","someone used the 'stats' command",[{key:"user",value:msg.author.username},{key:"mode",value:mode}])
APIEvents.onCommand("stats",true,msg.author,msg.channel,msg.guild,new Date())
})
if (!DISABLE.commands.slash.stats) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "stats") return
if (!interaction.guild) return
/**@type {"global"|"ticket"|"user"} */
const mode = interaction.options.getSubcommand(true)
await interaction.deferReply()
if (mode == "global"){
//send global stats
interaction.editReply({embeds:[await bot.statsManager.createGlobalStatsEmbed()]})
}else if (mode == "user"){
//send user stats
const mention = interaction.options.getUser("user",false)
if (mention){
//get user from mention
if (!bot.statsManager.existStats("user","TICKETS_CREATED",mention.id)){
interaction.editReply({embeds:[bot.errorLog.serverError(l.stats.errorUserNotFound)]})
return
}
interaction.editReply({embeds:[await bot.statsManager.createUserStatsEmbed(interaction.guild,mention,interaction.channel.id)]})
}else{
//use author as user
if (!bot.statsManager.existStats("user","TICKETS_CREATED",interaction.user.id)){
interaction.editReply({embeds:[bot.errorLog.serverError(l.stats.errorUserNotFound)]})
return
}
interaction.editReply({embeds:[await bot.statsManager.createUserStatsEmbed(interaction.guild,interaction.user,interaction.channel.id)]})
}
}else if (mode == "ticket"){
//send ticket stats
if (!bot.statsManager.existStats("ticket","STATUS",interaction.channel.id)){
interaction.editReply({embeds:[bot.errorLog.notInATicket]})
return
}
interaction.editReply({embeds:[await bot.statsManager.createTicketStatsEmbed(interaction.guild,interaction.channel.id)]})
}
log("command","someone used the 'stats' command",[{key:"user",value:interaction.user.username},{key:"mode",value:mode}])
APIEvents.onCommand("stats",true,interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-70
View File
@@ -1,70 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded ticket.js")
/**@type {String[]} */
var msgIds = []
config.messages.forEach((msg) => {
msgIds.push(msg.id)
})
if (!DISABLE.commands.text.msg) client.on("messageCreate", msg => {
if (msg.content.startsWith(config.prefix+"msg"||config.prefix+"message")){
if (!msg.guild) return
if (!permsChecker.global(msg.author.id,msg.guild.id)){
permsChecker.sendUserNoPerms(msg.author)
return
}
const id = msg.content.split(config.prefix+"msg")[1].substring(1) ? msg.content.split(config.prefix+"msg")[1].substring(1) : false
if (!id) return msg.channel.send({embeds:[bot.errorLog.invalidIdChooseFromList(msgIds,l.errors.missingArgsDescription+" `<id>`:\n`"+config.prefix+"msg <id>`")]})
if (!msgIds.includes(id)) return msg.channel.send({embeds:[bot.errorLog.invalidIdChooseFromList(msgIds,l.errors.missingArgsDescription+" `<id>`:\n`"+config.prefix+"msg <id>`")]})
const {embed,componentRows} = require("../core/utils/getEmbed").createEmbed(id)
msg.channel.send({embeds:[embed],components:componentRows})
log("command","someone used the 'message' command",[{key:"user",value:msg.author.username},{key:"id",value:id}])
APIEvents.onCommand("message",permsChecker.global(msg.author.id,msg.guild.id),msg.author,msg.channel,msg.guild,new Date())
}
})
if (!DISABLE.commands.slash.msg) client.on("interactionCreate", (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "message") return
if (!interaction.guild) return
if (!permsChecker.global(interaction.user.id,interaction.guild.id)){
permsChecker.sendUserNoPerms(interaction.user)
return
}
const id = interaction.options.getString("id")
if (!msgIds.includes(id)) return interaction.reply({embeds:[bot.errorLog.invalidIdChooseFromList(msgIds)]})
const {embed,componentRows} = require("../core/utils/getEmbed").createEmbed(id)
interaction.reply({content:l.commands.ticketWarning})
interaction.channel.send({embeds:[embed],components:componentRows})
log("command","someone used the 'message' command",[{key:"user",value:interaction.user.username},{key:"id",value:id}])
APIEvents.onCommand("message",permsChecker.global(interaction.user.id,interaction.guild.id),interaction.user,interaction.channel,interaction.guild,new Date())
})
}
-125
View File
@@ -1,125 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permsChecker = require("../core/utils/permisssionChecker")
const embed = discord.EmbedBuilder
const storage = bot.storage
const APIEvents = require("../core/api/modules/events")
const DISABLE = require("../core/api/api.json").disable
module.exports = () => {
bot.errorLog.log("debug","COMMANDS: loaded unclaim.js")
if (!DISABLE.commands.text.unclaim) client.on("messageCreate",msg => {
if (!msg.content.startsWith(config.prefix+"unclaim")) return
if (!msg.guild) return
const hiddendata = bot.hiddenData.readHiddenData(msg.channel.id)
if (hiddendata.length < 1) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(msg.author.id,msg.guild.id,ticketId)){
permsChecker.sendUserNoPerms(msg.author)
return
}
const unclaimuser = storage.get("claimData",msg.channel.id)
if (!unclaimuser || unclaimuser == "false") return msg.channel.send({embeds:[bot.errorLog.serverError("This ticket isn't claimed yet!")]})
hiddendata.push({key:"claimedby",value:"false"})
bot.hiddenData.writeHiddenData(msg.channel.id,hiddendata)
storage.set("claimData",msg.channel.id,"false")
msg.channel.messages.fetchPinned().then(msglist => {
/**@type {discord.Message} */
var firstmsg = msglist.last()
if (firstmsg == undefined || firstmsg.author.id != client.user.id) return msg.channel.send({embeds:[bot.errorLog.notInATicket]})
const newEmbed = new embed(firstmsg.embeds[0].data)
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
if (ticketData && ticketData.autoclose.enable){
const footerText = l.commands.autocloseTitle.replace("{0}",ticketData.autoclose.inactiveHours+"h")
newEmbed.setFooter({text:footerText})
}else{
newEmbed.setFooter(null)
}
if (firstmsg.components[0].components[1] && firstmsg.components[0].components[1].disabled){
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabled],embeds:[newEmbed]})
}else{
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowNormal],embeds:[newEmbed]})
}
msg.channel.send({embeds:[bot.embeds.commands.unclaimEmbed(msg.author)]})
log("command","someone used the 'unclaim' command",[{key:"user",value:msg.author.username}])
log("system","user unclaimed from ticket",[{key:"user",value:msg.author.username},{key:"ticket",value:msg.channel.name},{key:"unclaimed_user",value:unclaimuser}])
APIEvents.onTicketUnclaim(msg.author,msg.channel,msg.guild,new Date(),{status:"open",name:msg.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("unclaim",permsChecker.ticket(msg.author.id,msg.guild.id,ticketId),msg.author,msg.channel,msg.guild,new Date())
})
})
if (!DISABLE.commands.slash.unclaim) client.on("interactionCreate",async (interaction) => {
if (!interaction.isChatInputCommand()) return
if (interaction.commandName != "unclaim") return
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
interaction.reply({content:":x: "+l.errors.noPermsTitle,ephemeral:true})
return
}
await interaction.deferReply()
const unclaimuser = storage.get("claimData",interaction.channel.id)
if (!unclaimuser || unclaimuser == "false") return interaction.editReply({embeds:[bot.errorLog.serverError("This ticket isn't claimed yet!")]})
hiddendata.push({key:"claimedby",value:"false"})
bot.hiddenData.writeHiddenData(interaction.channel.id,hiddendata)
storage.set("claimData",interaction.channel.id,"false")
interaction.channel.messages.fetchPinned().then(msglist => {
/**@type {discord.Message} */
var firstmsg = msglist.last()
if (firstmsg == undefined || firstmsg.author.id != client.user.id) return interaction.editReply({embeds:[bot.errorLog.notInATicket]})
const newEmbed = new embed(firstmsg.embeds[0].data)
const ticketData = require("../core/utils/configParser").getTicketById(ticketId,true)
if (ticketData && ticketData.autoclose.enable){
const footerText = l.commands.autocloseTitle.replace("{0}",ticketData.autoclose.inactiveHours+"h")
newEmbed.setFooter({text:footerText})
}else{
newEmbed.setFooter(null)
}
if (firstmsg.components[0].components[1] && firstmsg.components[0].components[1].disabled){
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabled],embeds:[newEmbed]})
}else{
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowNormal],embeds:[newEmbed]})
}
interaction.editReply({embeds:[bot.embeds.commands.unclaimEmbed(interaction.user)]})
log("command","someone used the 'unclaim' command",[{key:"user",value:interaction.user.username}])
log("system","user unclaimed from ticket",[{key:"user",value:interaction.user.username},{key:"ticket",value:interaction.channel.name},{key:"unclaimed_user",value:unclaimuser}])
APIEvents.onTicketUnclaim(interaction.user,interaction.channel,interaction.guild,new Date(),{status:"open",name:interaction.channel.name,ticketOptions:ticketData})
APIEvents.onCommand("unclaim",permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId),interaction.user,interaction.channel,interaction.guild,new Date())
})
})
}
-148
View File
@@ -1,148 +0,0 @@
{
"_DOCUMENTATION":"https://otdocs.dj-dj.be",
"_SUPPORT":"https://discord.dj-dj.be",
"color":"#ffffff",
"serverId":"server id (for slash cmds)",
"token":{
"value":"insert your bot token here",
"fromENV":false
},
"adminRoles":["discord role id"],
"prefix":"!ticket ",
"languageFile":"english",
"status":{
"enabled":true,
"type":"PLAYING",
"text":"!ticket help"
},
"system":{
"maxAmountOfTickets":3,
"dmMessages":false,
"memberRole":"member role (leave empty if you have no member role)",
"closeMode":"adminonly OR normal (select one!)",
"transcripts":"!!!! ALL SETTINGS FOR TRANSCRIPTS ARE LOCATED IN: transcriptconfig.json !!!!",
"showSlashcmdsInHelp":false,
"answerInEphemeralOnOpen":true
},
"options":[
{
"id":"example-ticket",
"name":"Question",
"description":"Create this ticket if you have a question.",
"icon":"🎫",
"label":"question",
"type":"ticket",
"color":"gray",
"adminroles":["role id"],
"readonlyAdminroles":["role id"],
"channelprefix":"question-",
"category":"category id (or leave empty)",
"message":"You created a ticket!",
"enableDmOnOpen":true,
"ticketmessage":"The staff will help you soon!\n\n*Click on the button below to close the ticket!*",
"thumbnail":{
"enable":false,
"url":"https://www.example.com/catmemes/cat.png"
},
"image":{
"enable":false,
"url":"https://www.example.com/catmemes/cat.png"
},
"closedCategory":{
"enable":false,
"id":"category id"
},
"autoclose":{
"enable":false,
"inactiveHours":24
},
"ping":{
"@here":true,
"@everyone":false,
"custom":{
"enable":false,
"roleId":"012345678910"
}
}
},
{
"id":"example-website",
"name":"Website",
"description":"Go to our website.",
"icon":"✅",
"label":"click here",
"type":"website",
"url":"https://www.dj-dj.be"
},
{
"id":"example-role",
"name":"Update Ping",
"description":"Click here to get pinged for updates!",
"icon":"📢",
"label":"Update Ping",
"type":"role",
"color":"red",
"roles":["discord role id"],
"mode":"add&remove|remove|add",
"enableDmOnOpen":true
}
],
"messages":[
{
"id":"example-embed",
"name":"Choose Your Ticket",
"description":"You can click one of the buttons below!",
"dropdown":false,
"enableFooter":false,
"footer":"Open Ticket v3.5.7 - I'm a footer!",
"enableFooterImage":false,
"footerImage":"https://www.example.com/catmemes/cat.png",
"enableThumbnail":false,
"thumbnail":"https://www.example.com/catmemes/cat.png",
"enableImage":false,
"image":"https://www.example.com/catmemes/cat.png",
"enableCustomColor":false,
"color":"#ffffff",
"options":["example-ticket","example-website","example-role"],
"other":{
"enableTicketExplaination":true,
"enableMaxTicketsWarning":true,
"customDropdownPlaceholder":{
"enable":false,
"text":"Choose a ticket"
},
"customCategoryText":{
"enable":false,
"text":"choose a category:"
},
"embedTitleURL":{
"enable":false,
"url":"https://www.dj-dj.be/projects/open-ticket"
}
}
}
]
}
+175
View File
@@ -0,0 +1,175 @@
/*
* Hi there! Thank you for installing Open Ticket.
* ----------------------------------------------
* If you need any assistance with configuring the bot,
* feel free to use the documentation or join our Discord server:
* https://otdocs.dj-dj.be
* https://discord.dj-dj.be
* ----------------------------------------------
* SETUP:
* 1. Install the required dependencies using the command: "npm install"
* 2. Configure the bot in one of the following ways:
* a. (easy) Using the Quick Setup CLI Tool
* b. (difficult) Using the JSON files in `./config/`
*
* Start the Quick Setup CLI Tool using the command: "npm run setup"
* After configuration, start the bot using the command: "npm start"
*
* Good luck! DJj123dj & contributors
*/
{
"_CONFIG_VERSION":"open-ticket-v4.2.0",
/* Load the bot token from .env or the "token" field below. Leave "token" empty if using "tokenFromENV". */
"token":"INSERT_BOT_TOKEN",
"tokenFromENV":false,
"mainColor":"#f8ba00", //Hex color used in most embeds
"language":"english", //Visit README.md for list
"prefix":"!ticket ", //Prefix used in text commands
"serverId":"DISCORD_SERVER_ID",
"globalAdmins":["DISCORD_ROLE_ID"], //Have access to all commands
/* Enable/disable text or slash commands. */
"slashCommands":true,
"textCommands":true,
/* Configure the status of the bot. */
"status":{
"enabled":true,
"type":"listening", //Choices: listening, watching, playing, custom
"mode":"online", //Choices: online, invisible, idle, dnd
"text":"/help",
"state":"" //Additional text (Leave empty to disable)
},
/* Send ticket logs to a channel or in DM of the ticket creator. */
"logs":{
"enabled":false,
"channel":"DISCORD_CHANNEL_ID",
"logMessages":{
"creation":{"dm":false,"logs":true},
"closing":{"dm":false,"logs":true},
"deleting":{"dm":false,"logs":true},
"reopening":{"dm":false,"logs":true},
"claiming":{"dm":false,"logs":true},
"pinning":{"dm":false,"logs":true},
"adding":{"dm":false,"logs":true},
"removing":{"dm":false,"logs":true},
"renaming":{"dm":false,"logs":true},
"moving":{"dm":false,"logs":true},
"blacklisting":{"dm":false,"logs":true},
"transferring":{"dm":false,"logs":true},
"topicChange":{"dm":false,"logs":true},
"priorityChange":{"dm":false,"logs":true},
"reactionRole":{"dm":false,"logs":true}
}
},
/* A large collection of settings for the ticket system. */
"ticketSystem":{
"preferSlashOverText":true, //Show slashcmds in help menu's
"sendErrorOnUnknownCommand":true, //Send error when command not found
"questionFieldsInCodeBlock":true, //Put question answers in code blocks
"displayFieldsWithQuestions":false, //Display embed fields together with question answers
"showGlobalAdminsInPanelRoles":false, //Include "globalAdmins" in panel admin lists
"disableVerifyBars":false, //Disable the (❌/✅) buttons
"useRedErrorEmbeds":true, //Make errors embeds always red
"alwaysShowReason":false, //Show reason even if none is provided
"emojiStyle":"before", //The style of emoji's in embeds. Choices: before, after, double, disabled
"pinEmoji":"📌", //Channel emoji of pinned tickets (Leave empty to disable)
"closeEmoji":"", //Channel emoji of closed tickets (Leave empty to disable)
"replyOnTicketCreation":true, //Reply with a msg when a ticket is created
"replyOnReactionRole":true, //Reply with a msg when a reaction role is used
"askPriorityOnTicketCreation":true, //Show a dropdown to select priority
"removeParticipantsOnClose":false, //Remove non-admins when ticket is closed
"disableAutocloseAfterReopen":true, //Disable autoclose after ticket got reopened
"autodeleteRequiresClosedTicket":true, //A ticket must be closed before autodelete works
"adminOnlyDeleteWithoutTranscript":true, //Only allow "globalAdmins" to delete a ticket without transcript
"allowCloseBeforeMessage":false, //Allow closing before a message is sent
"allowCloseBeforeAdminMessage":true, //Allow closing before an admin has sent a message
"useTranslatedConfigChecker":true, //Translate config errors in the console
"pinFirstTicketMessage":true, //Pin the ticket message to the channel
/* Enable/disable certain buttons & features of the bot. */
"enableTicketClaimButtons":true,
"enableTicketCloseButtons":true,
"enableTicketPinButtons":true,
"enableTicketDeleteButtons":true,
"enableTicketActionWithReason":true,
"enableDeleteWithoutTranscript":true, //Allow deleting tickets without transcript
"enableCreateTicketForOtherUser":true, //Allow creating tickets for other users
/* Set the maximum amount of simultaneous tickets. */
"limits":{
"enabled":true,
"globalMaximum":50,
"userMaximum":3
},
/* Choose which data is shown in the channel topic. */
"channelTopic":{
"showOptionName":true,
"showOptionDescription":false,
"showOptionTopic":true,
"showPriority":false,
"showClosed":true,
"showClaimed":false,
"showPinned":false,
"showCreator":false,
"showParticipants":false
},
/* Move closed tickets to a separate category. */
"closedCategory":{
"enabled":false,
"categoryId":"DISCORD_CATEGORY_ID"
},
/* Create tickets in a backup category when the original category exceeds 50 channels. */
"backupCategory":{
"enabled":false,
"categoryId":"DISCORD_CATEGORY_ID"
},
/* Move claimed tickets to a matching category of the user that claimed the ticket. Set to empty list [] to disable. */
"claimedCategories":[
{"user":"DISCORD_USER_ID","category":"DISCORD_CATEGORY_ID"}
]
},
/*
* Set permissions for each individual command, button or action.
* CHOICES:
* >> "none" -> Command disabled
* >> "everyone" -> Allowed for everyone
* >> "admin" -> Global & ticket admins only
* >> "DISCORD_ROLE_ID" -> Custom role only
*/
"permissions":{
"help":"everyone",
"panel":"admin",
"ticket":"none",
"close":"everyone",
"delete":"admin",
"reopen":"everyone",
"claim":"admin",
"unclaim":"admin",
"pin":"admin",
"unpin":"admin",
"move":"admin",
"rename":"admin",
"add":"admin",
"remove":"admin",
"blacklist":"admin",
"stats":"everyone",
"clear":"admin",
"autoclose":"admin",
"autodelete":"admin",
"transfer":"admin",
"topic":"admin",
"priority":"admin",
"transcripts":"admin"
}
}
+165
View File
@@ -0,0 +1,165 @@
/*
* OPEN TICKET BUTTON OPTIONS
* ----------------------------------------------
* Create customizable ticket, website, reaction-role or sub-panel button options.
* Up to 25 options can be added to each panel in (config/panels.jsonc)
* There are 4 types of options available: ticket, website, role, sub-panel
*
* TIP: Create new options by copying everything between and including the {...} brackets of an option. Paste it after the last option and make sure that they are seperated by a comma.
*/
[
{
/* A ticket option creates a button to open a ticket. */
"id":"example-ticket",
"name":"Question",
"description":"Want to tell us something? Create this ticket for general questions for our support team.", //Leave empty to disable
"type":"ticket",
"button":{
/* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
"emoji":"🎫",
"label":"Question",
"color":"gray" //Choices: gray, red, green, blue
},
/* Add up to 5 modal questions IDs from (config/questions.jsonc). */
"questions":["example-question-1","example-question-2"],
"ticketAdmins":["DISCORD_ROLE_ID"],
"readonlyAdmins":["DISCORD_ROLE_ID"],
"allowCreationByBlacklistedUsers":false,
"channel":{
/* Configure the name, topic and category of the ticket option. */
"prefix":"question-",
"suffix":"user-name", //Choices: user-name, user-id, random-number, random-hex, counter-dynamic, counter-fixed
"category":"DISCORD_CATEGORY_ID", //Leave empty to disable
"topic":"The ticket topic shown in the channel." //Leave empty to disable
},
"dmMessage":{
/* Send a customisable message in DM when creating a ticket. */
"enabled":false,
"text":"", //Leave empty to disable
"embed":{
"enabled":false,
"title":"Question Ticket", //Leave empty to disable
"description":"Thank you for creating a ticket in our server. We will try to help you as soon as possible.", //Leave empty to disable
"customColor":"#f8ab00", //Leave empty to use default color
"image":"", //Image URL. Leave empty to disable
"thumbnail":"", //Image URL. Leave empty to disable
/* Embed fields. Set to empty list [] to disable. */
"fields":[
{"name":"FIELD_NAME","value":"FIELD_VALUE","inline":false}
],
"timestamp":false
}
},
"ticketMessage":{
/* Send a customisable message in the ticket with close, claim, delete, ... buttons. */
"enabled":true,
"text":"", //Leave empty to disable
"embed":{
"enabled":true,
"title":"Question Ticket", //Leave empty to disable
"description":"Thank you for creating a ticket in our server.\nOur support team will assist you as soon as possible.", //Leave empty to disable
"customColor":"#f8ab00", //Leave empty to use default color
"image":"", //Image URL. Leave empty to disable
"thumbnail":"", //Image URL. Leave empty to disable
/* Embed fields. Set to empty list [] to disable. */
"fields":[
{"name":"FIELD_NAME","value":"FIELD_VALUE","inline":false}
],
"timestamp":false
},
"ping":{
/* Customise the user & role mentions of this ticket message. */
"@here":true,
"@everyone":false,
"custom":["DISCORD_ROLE_ID"]
}
},
"autoclose":{
/* Autoclose this ticket after a period of inactivity or when the creator leaves the server. */
"enableInactiveHours":false,
"inactiveHours":24,
"enableUserLeave":false,
"disableOnClaim":false
},
"autodelete":{
/* Autodelete this ticket after a period of inactivity or when the creator leaves the server. */
"enableInactiveDays":false,
"inactiveDays":7,
"enableUserLeave":false,
"disableOnClaim":false
},
"cooldown":{
/* Users must wait a certain period before being able to create another ticket of this type. */
"enabled":false,
"cooldownMinutes":10
},
"limits":{
/* Set the maximum amount of simultaneous tickets of this option. */
"enabled":false,
"globalMaximum":20,
"userMaximum":3
},
"slowMode":{
/* Enable slow-mode in the ticket channel. */
"enabled":false,
"slowModeSeconds":20
}
},
{
/* A website option creates a button with a URL to an external website. */
"id":"example-website",
"name":"Website",
"description":"Take a look at our amazing website.", //Leave empty to disable
"type":"website",
"button":{
/* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
"emoji":"😃",
"label":"Visit Website"
},
"url":"https://www.dj-dj.be"
},
{
/* A reaction-role option creates a button for members to choose roles. */
"id":"example-role",
"name":"Update Ping",
"description":"Receive notifications about updates in our server.", //Leave empty to disable
"type":"role",
"button":{
/* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
"emoji":"📢",
"label":"Update Ping",
"color":"gray" //Choices: gray, red, green, blue
},
"roles":["DISCORD_ROLE_ID"],
"mode":"add&remove OR remove OR add", //What to do with the roles. Choices: add&remove, add, remove
"removeRolesOnAdd":["DISCORD_ROLE_ID"], //Remove these old roles when new roles are added.
"addOnMemberJoin":false //Add these roles automatically when joining the server.
},
{
/* A sub-panel option creates a button which sends another panel for additional options. */
"id":"example-sub-panel",
"name":"Example Sub-Panel",
"description":"This is an example of how to implement a sub-panel in Open Ticket.", //Leave empty to disable
"type":"sub-panel",
"button":{
/* Configure the button style of this option. At least one of "emoji" or "label" must be provided. */
"emoji":"📋",
"label":"Sub-Panel Example",
"color":"gray" //Choices: gray, red, green, blue
},
"subPanelId":"example-panel" //Choose a panel ID from (config/panels.jsonc)
}
]
+57
View File
@@ -0,0 +1,57 @@
/*
* OPEN TICKET PANELS
* ----------------------------------------------
* Create customizable panel messages with buttons or a dropdown.
* Add up to 25 options to this panel from (config/options.jsonc)
* Panels can be customised with text, images, colors and more.
*
* Spawn the panel in Discord using: /panel <id>
*
* TIP: Create new panels by copying everything between and including the {...} brackets of an panel. Paste it after the last panel and make sure that they are seperated by a comma.
*/
[
{
/* A panel is creates a message with up to 25 options as buttons or dropdown. */
"id":"example-panel",
"name":"Example Panel",
"dropdown":false,
/* Add up to 5 option IDs from (config/options.jsonc). */
"options":["example-ticket","example-website","example-role"],
"text":"", //Leave empty to disable
"embed":{
"enabled":true,
"title":"Tickets:", //Leave empty to disable
"description":"Create a ticket by selecting one of the options below. Once selected, a private ticket will be created for you and our support team will be able to assist you directly.", //Leave empty to disable
"customColor":"#f8ab00", //Leave empty to use default color
"url":"", //URL. Leave empty to disable
"image":"", //Image URL. Leave empty to disable
"thumbnail":"", //Image URL. Leave empty to disable
"footer":"Open Ticket v4.2.0", //Leave empty to disable
/* Embed fields. Set to empty list [] to disable. */
"fields":[
{"name":"Field name","value":"Field value","inline":false}
],
"timestamp":false
},
"settings":{
"dropdownPlaceholder":"Create a ticket...", //Leave empty to use default.
"maximumButtonsPerRow":5,
/* Display the maximum amount of tickets per user. */
"enableMaxTicketsWarningInText":false,
"enableMaxTicketsWarningInEmbed":true,
/* Automatically generate option descriptions from (config/options.jsonc). */
"describeOptionsLayout":"normal", //Choices: simple, normal, detailed
"describeOptionsCustomTitle":"",
"describeOptionsInText":false,
"describeOptionsInEmbedFields":true,
"describeOptionsInEmbedDescription":false
}
}
]
+102
View File
@@ -0,0 +1,102 @@
/*
* OPEN TICKET MODAL QUESTIONS
* ----------------------------------------------
* Create customizable modal questions that will be shown before creating a ticket.
* Each ticket option (config/options.jsonc) can contain a maximum of 5 questions.
* There are 6 types of questions available: short, paragraph, dropdown, radio-select, checkbox-select, text-display
*
* TIP: Create new questions by copying everything between and including the {...} brackets of a question. Paste it after the last question and make sure that they are seperated by a comma.
*/
[
{
/* A short text input modal question. */
"id":"example-question-1",
"name":"Example Question 1",
"description":"", //Leave empty to disable
"type":"short",
"required":true,
"placeholder":"Insert answer...",
"length":{
/* Configure length limits for the answer. */
"enabled":false,
"min":0,
"max":1000
}
},
{
/* A paragraph text input modal question. */
"id":"example-question-2",
"name":"Example Question 2",
"description":"", //Leave empty to disable
"type":"paragraph",
"required":false,
"placeholder":"Insert answer...",
"length":{
/* Configure length limits for the answer. */
"enabled":false,
"min":0,
"max":1000
}
},
{
/* A dropdown menu input modal question with up to 25 choices. "emoji" & "description" fields are optional. */
"id":"example-question-3",
"name":"Example Question 3",
"description":"This is a dropdown question.", //Leave empty to disable
"type":"dropdown",
"required":false,
"placeholder":"Choose your answer...",
"choices":[
{"title":"Choice A","description":"Apple","emoji":"🍎"},
{"title":"Choice B","description":"Banana","emoji":"🍌"},
{"title":"Choice C","description":"Orange","emoji":"🍊"},
{"title":"Choice D","description":"Kiwi","emoji":"🥝"}
]
},
{
/* A radio select input modal question with up to 10 choices. */
"id":"example-question-4",
"name":"Example Question 4",
"description":"This is a radio select question.", //Leave empty to disable
"type":"radio-select",
"required":true,
"choices":[
{"title":"Choice A","description":"Up","selectedByDefault":false},
{"title":"Choice B","description":"Down","selectedByDefault":false},
{"title":"Choice C","description":"Left","selectedByDefault":false},
{"title":"Choice D","description":"Right","selectedByDefault":false}
]
},
{
/* A checkbox select input modal question with up to 10 choices. */
"id":"example-question-5",
"name":"Example Question 5",
"description":"This is a checkbox select question.", //Leave empty to disable
"type":"checkbox-select",
"required":true,
"limits":{
/* Configure checkbox amount limits for the answer. */
"enabled":false,
"min":0,
"max":10
},
"choices":[
{"title":"Choice A","description":"Happiness","selectedByDefault":false},
{"title":"Choice B","description":"Anger","selectedByDefault":false},
{"title":"Choice C","description":"Sadness","selectedByDefault":false},
{"title":"Choice D","description":"Fear","selectedByDefault":false}
]
},
{
/* Show text in a modal to provide extra details or explain questions. */
"id":"example-text-display",
"type":"text-display",
"textContents":"This is a text display. It isn't a question, but allows you to display additional details."
}
]
+70
View File
@@ -0,0 +1,70 @@
/*
* OPEN TICKET TRANSCRIPTS
* ----------------------------------------------
* Enable transcript creation when tickets are deleted. There are 2 available transcript types: HTML & Text
*
* HTML Transcripts (recommended):
* Generate transcripts as HTML files to view in the browser. No server or domain required. HTML Transcripts use an external service to process and host the transcripts.
*
* Text Transcripts:
* Generate transcripts as simple .txt files with limited details. Processing happens fully local.
*/
{
"general":{
"enabled":false,
/* Choose which users and channel get the generated transcript. */
"enableChannel":false,
"enableCreatorDM":false,
"enableParticipantDM":false,
"enableActiveAdminDM":false,
"enableEveryAdminDM":false,
"channel":"DISCORD_CHANNEL_ID", //Where to send transcripts. Leave empty when disabled.
"mode":"html" //The type of transcript to use. Choices: html, text
},
"embedSettings":{
/* Customise the embed which contains the generated transcript file or URL. */
"customColor":"#f8ab00", //Leave empty to use default color
"listAllParticipants":false,
"includeTicketStats":false
},
"textTranscriptStyle":{
/* Customise layout of the text transcripts. */
"layout":"normal", //Choices: simple, normal, detailed
"includeStats":true,
"includeIds":false,
"includeEmbeds":true,
"includeFiles":true,
"includeBotMessages":true,
"fileMode":"custom", //How to name the transcript file? Choices: custom, channel-name, channel-id, user-name, user-id
"customFileName":"transcript" //Custom filename without extension
},
"htmlTranscriptStyle":{
/* Customise layout of the HTML transcripts. */
"background":{
"enableCustomBackground":false,
"backgroundColor":"#f8ba00", //Leave empty to use Open Ticket color (#f8ba00)
"backgroundImage":"https://www.example.com/image.png" //Image URL to fill entire background. Leave empty to disable
},
"header":{
"enableCustomHeader":false,
"backgroundColor":"#202225",
"decoColor":"#f8ba00",
"textColor":"#ffffff"
},
"stats":{
"enableCustomStats":false,
"backgroundColor":"#202225",
"keyTextColor":"#737373",
"valueTextColor":"#ffffff",
"hideBackgroundColor":"#40444a",
"hideTextColor":"#ffffff"
},
"favicon":{
"enableCustomFavicon":false,
"imageUrl":"https://t.dj-dj.be/favicon.png"
}
}
}
-18
View File
@@ -1,18 +0,0 @@
//base
const bot = require("../../index")
exports.client = bot.client
exports.config = bot.config
exports.utils = {
log:bot.errorLog.log,
storage:bot.storage
}
//events
const events = require("./modules/events").events
exports.events = events
//actions
const actions = require("./modules/actions").actions
//comming soon
//exports.actions = actions
-61
View File
@@ -1,61 +0,0 @@
{
"version":"1.0.0",
"enableAPIdebug":false,
"enableAPIconsolelogs":false,
"info":"If you turn these switches to true, then the feature will be turned OFF!",
"info2":"You can use this to disable some features you don't want!",
"disable":{
"messages":{
"DISCLAIMER":"the messages part doesn't work yet!",
"ticketCreated":false,
"ticketClosed":false,
"ticketDeleted":false,
"ticketReopened":false
},
"transcripts":{
"server":false,
"dm":false
},
"commands":{
"text":{
"help":false,
"msg":false,
"close":false,
"delete":false,
"reopen":false,
"new":false,
"add":false,
"remove":false,
"rename":false,
"claim":false,
"unclaim":false,
"change":false,
"stats":false
},
"slash":{
"help":false,
"msg":false,
"close":false,
"delete":false,
"reopen":false,
"new":false,
"add":false,
"remove":false,
"rename":false,
"claim":false,
"unclaim":false,
"change":false,
"stats":false
}
},
"checkerjs":{
"token":false,
"all":false
},
"debug":{
"all":false,
"debuglogs":false
}
}
}
-7
View File
@@ -1,7 +0,0 @@
//ACTIONS:
//EXPORT ACTIONS:
exports.actions = {
}
-454
View File
@@ -1,454 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const configParser = require("../../utils/configParser")
//EVENT API:
const ticketOpenListeners = []
const ticketCloseListeners = []
const ticketDeleteListeners = []
const ticketReopenListeners = []
const ticketAddListeners = []
const ticketRemoveListeners = []
const ticketClaimListeners = []
const ticketUnclaimListeners = []
const ticketChangeListeners = []
const transcriptCreationListeners = []
const commandListeners = []
const reactionRoleListeners = []
const errorListeners = []
/**
*
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:Object|false}} ticketdata
*/
exports.onTicketOpen = (user,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","new ticket created",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketOpenListeners.forEach((func) => {
try {
func(user,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
* @param {String} reason
*/
exports.onTicketClose = (user,channel,guild,date,ticketdata,reason) => {
//system
bot.errorLog.log("api","ticket closed",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)},{key:"reason",value:reason}])
ticketCloseListeners.forEach((func) => {
try {
func(user,channel,guild,date,ticketdata,reason)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketDelete = (user,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","ticket deleted",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketDeleteListeners.forEach((func) => {
try {
func(user,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketReopen = (user,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","ticket reopened",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketReopenListeners.forEach((func) => {
try {
func(user,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.User} editeduser
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketAdd = (user,editeduser,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","user added to ticket",[{key:"userid",value:user.id},{key:"editeduser",value:editeduser.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketAddListeners.forEach((func) => {
try {
func(user,editeduser,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.User} editeduser
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketRemove = (user,editeduser,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","user removed from ticket",[{key:"userid",value:user.id},{key:"editeduser",value:editeduser.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketRemoveListeners.forEach((func) => {
try {
func(user,editeduser,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketClaim = (user,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","user claimed a ticket",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketClaimListeners.forEach((func) => {
try {
func(user,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketUnclaim = (user,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","user unclaimed a ticket",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketUnclaimListeners.forEach((func) => {
try {
func(user,channel,guild,date,ticketdata)
}catch{}
})
}
/**
* @param {String} newtype
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
exports.onTicketChange = (newtype,user,channel,guild,date,ticketdata) => {
//system
bot.errorLog.log("api","user changed ticket type",[{key:"newtype",value:newtype},{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}])
ticketChangeListeners.forEach((func) => {
try {
func(newtype,user,channel,guild,date,ticketdata)
}catch{}
})
}
/**
*
* @param {discord.Message[]} messages
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
*/
exports.onTranscriptCreation = (messages,channel,guild,date) => {
//system
bot.errorLog.log("api","transcript created",[{key:"messages",value:JSON.stringify(messages)},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id}])
transcriptCreationListeners.forEach((func) => {
try {
func(messages,channel,guild,date)
}catch{}
})
}
/**@typedef {"help"|"message"|"close"|"delete"|"reopen"|"rename"|"add"|"remove"|"claim"|"unclaim"|"change"|"stats"} OTCommandType */
/**
*
* @param {OTCommandType} type
* @param {Boolean} hasPerms
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
*/
exports.onCommand = (type,hasPerms,user,channel,guild,date) => {
const newguild = guild ? guild : false
//system
bot.errorLog.log("api","someone used a command",[{key:"type",value:type},{key:"hasPerms",value:hasPerms},{key:"userid",value:user},{key:"channelid",value:channel.id},{key:"guildid",value:newguild.id}])
commandListeners.forEach((func) => {
try {
func(type,hasPerms,user,channel,newguild,date)
}catch{}
})
}
/**@typedef {"add"|"remove"|"add&remove"} OTReactionRoleType */
/**@typedef {"add"|"remove"} OTReactionRoleMode */
/**
* @param {OTReactionRoleMode} mode
* @param {OTReactionRoleType} type
* @param {discord.Role} role
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
*/
exports.onReactionRole = (mode,type,role,user,channel,guild,date) => {
//system
bot.errorLog.log("api","someone used a reaction role",[{key:"mode",value:mode},{key:"type",value:type},{key:"roleid",value:role.id},{key:"userid",value:user},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id}])
reactionRoleListeners.forEach((func) => {
try {
func(mode,type,role,user,channel,guild,date)
}catch{}
})
}
/**
* @param {String} err
* @param {Date} date
*/
exports.onError = (err,date) => {
//system
bot.errorLog.log("api","there is an error",[{key:"error",value:err}])
errorListeners.forEach((func) => {
try {
func(err,date)
}catch{}
})
}
//EVENT CALLBACKS:
/**
* @callback TicketActionEvent
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
/**
* @callback TicketCloseEvent
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
* @param {String} reason
*/
/**
* @callback TranscriptCreationEvent
* @param {discord.Message[]} messages
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
*/
/**
* @callback TicketUserChangeEvent
* @param {discord.User} user
* @param {discord.User} editeduser
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
/**
* @callback TicketTypeChangeEvent
* @param {String} newtype
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
* @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:configParser.OTTicketOptions}} ticketdata
*/
/**
* @callback CommandEvent
* @param {OTCommandType} type
* @param {Boolean} hasPerms
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild|false} guild
* @param {Date} date
*/
/**
* @callback ReactionRoleEvent
* @param {OTReactionRoleMode} mode
* @param {OTReactionRoleType} type
* @param {discord.Role} role
* @param {discord.User} user
* @param {discord.TextChannel} channel
* @param {discord.Guild} guild
* @param {Date} date
*/
/**
* @callback ErrorEvent
* @param {String} error
* @param {Date} date
*/
//EVENT EXPORT:
/**@param {TicketActionEvent} callback */
const onTicketOpen = (callback) => {
ticketOpenListeners.push(callback)
}
/**@param {TicketCloseEvent} callback */
const onTicketClose = (callback) => {
ticketCloseListeners.push(callback)
}
/**@param {TicketActionEvent} callback */
const onTicketDelete = (callback) => {
ticketDeleteListeners.push(callback)
}
/**@param {TicketActionEvent} callback */
const onTicketReopen = (callback) => {
ticketReopenListeners.push(callback)
}
/**@param {TicketUserChangeEvent} callback */
const onTicketAdd = (callback) => {
ticketAddListeners.push(callback)
}
/**@param {TicketUserChangeEvent} callback */
const onTicketRemove = (callback) => {
ticketRemoveListeners.push(callback)
}
/**@param {TicketActionEvent} callback */
const onTicketClaim = (callback) => {
ticketClaimListeners.push(callback)
}
/**@param {TicketActionEvent} callback */
const onTicketUnclaim = (callback) => {
ticketUnclaimListeners.push(callback)
}
/**@param {TicketTypeChangeEvent} callback */
const onTicketChange = (callback) => {
ticketChangeListeners.push(callback)
}
/**@param {TranscriptCreationEvent} callback */
const onTranscriptCreation = (callback) => {
transcriptCreationListeners.push(callback)
}
/**@param {CommandEvent} callback */
const onCommand = (callback) => {
commandListeners.push(callback)
}
/**@param {ReactionRoleEvent} callback */
const onReactionRole = (callback) => {
commandListeners.push(callback)
}
/**@param {ErrorEvent} callback */
const onError = (callback) => {
errorListeners.push(callback)
}
exports.events = {
onTicketOpen,
onTicketClose,
onTicketDelete,
onTicketReopen,
onTicketAdd,
onTicketRemove,
onTicketClaim,
onTicketUnclaim,
onTicketChange,
onTranscriptCreation,
onCommand,
onReactionRole,
onError
}
-43
View File
@@ -1,43 +0,0 @@
const fs = require("fs")
const bot = require("../../index")
const log = bot.errorLog.log
module.exports = () => {
const args = process.argv
const debugMode = args.includes("--debug")
const files = fs.readdirSync("./plugins")
const api = require("./api.json")
var totalcount = 0
var successcount = 0
var failcount = 0
files.forEach((file) => {
if (file.endsWith(".plugin.js")){
try {
require("../../plugins/" + file)()
successcount++
totalcount++
}catch(err){
/**@type {Error} */
const theError = err
if (api.enableAPIdebug || debugMode) {
console.error(theError)
}
try {
const data = fs.readFileSync("./openticketdebug.txt")
const newData = data ? data.toString() : ""
fs.writeFileSync("./openticketdebug.txt", newData + "\nPLUGIN "+file+" ERROR: " + theError.name + ": " + theError.message + "\n" + theError.stack + "\n\n")
} catch {
fs.writeFileSync("./openticketdebug.txt", "\nPLUGIN "+file+" ERROR: " + theError.name + ": " + theError.message + "\n" + theError.stack + "\n\n")
}
failcount++
totalcount++
}
}
})
require("../startscreen").headerDataPlugins({ total: totalcount, success: successcount, error: failcount })
}
-551
View File
@@ -1,551 +0,0 @@
exports.checker = async () => {
const chalk = await (await import("chalk")).default
if (process.argv.some((v) => v == "--nochecker")) return
if (process.argv.some((v) => v == "--devconfig")){
//console.log(chalk.blue("=> used dev config instead of normal config"))
try{
var tempconfig = require("../devconfig.json")
}catch(err){console.log(err);var tempconfig = require("../config.json")}
}else{
var tempconfig = require("../config.json")
}
const config = tempconfig
var errorList = []
var isError = false
var warnList = []
var isWarn = false
const createError = (message) => {
isError = true
errorList.push("open-ticket CONFIG ERROR: "+message)
}
const createWarn = (message) => {
isWarn = true
warnList.push("open-ticket CONFIG WARNING: "+message)
}
//validators
/**@param {"userid"|"roleid"|"channelid"|"serverid"|"categoryid"} mode @param {String} value @param {String} path */
const checkDiscord = (mode,value,path) => {
if (mode == "userid"){
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+"' | this user id is invalid")
}
}else if (mode == "channelid"){
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+"' | this channel id is invalid")
}
}else if (mode == "roleid"){
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+"' | this role id is invalid")
}
}else if (mode == "serverid"){
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+"' | this server id is invalid")
}
}else if (mode == "categoryid"){
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+"' | this category id is invalid")
}
}
}
/**@param {String} value */
const checkToken = (value) => {
if (value.length < 40){
createError("'token/value' | your token is invalid")
}
if (value.includes(" ")) createError("'token/value' | your token includes spaces!")
}
/**@param {String} value @param {String} path */
const checkHexColor = (value,path) => {
if (!/^#[a-fA-F0-9]{3,6}$/.test(value)){
if (value.length < 4) return createError("'"+path+"' | hex color too short! (example: #123abc)")
if (value.length > 7) return createError("'"+path+"' | hex color too long! (example: #123abc)")
createError("'"+path+"' | invalid color! (example: #123abc)")
}
}
/**@param {String} value @param {String} path */
const checkEmbedColor = (value,path) => {
if (!['DEFAULT','WHITE','AQUA','GREEN','BLUE','YELLOW','PURPLE','LUMINOUS_VIVID_PINK','FUCHSIA','GOLD','ORANGE','RED','GREY','DARKER_GREY','NAVY','DARK_AQUA','DARK_GREEN','DARK_BLUE','DARK_PURPLE','DARK_VIVID_PINK','DARK_GOLD','DARK_ORANGE','DARK_RED','DARK_GREY','LIGHT_GREY','DARK_NAVY','BLURPLE','GREYPLE','DARK_BUT_NOT_BLACK','NOT_QUITE_BLACK','RANDOM'].includes(value)){
return createError("'"+path+"' | invalid color, must be a hex code or default color (more info in the wiki)")
}
}
/**@param {String} value @param {String} path @param {Number} minLength @param {Number} maxLength @param {String} name */
const checkString = (value,minLength,maxLength,path,name) => {
if (value.length < minLength){
createError("'"+path+"' | "+name+" is too short!")
}else if (value.length > maxLength){
createError("'"+path+"' | "+name+" is too long!")
}
}
/**@param {"userid"|"roleid"|"channelid"|"serverid"} mode @param {String[]} arrayValue @param {String} path */
const checkDiscordArray = (mode,arrayValue,path) => {
if (!Array.isArray(arrayValue)){
return createError("'"+path+"' | this needs to be an Array!")
}
if (mode == "userid"){
arrayValue.forEach((value,index) => {
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+":"+index+"' | this user id is invalid")
}
})
}else if (mode == "channelid"){
arrayValue.forEach((value,index) => {
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+":"+index+"' | this channel id is invalid")
}
})
}else if (mode == "roleid"){
arrayValue.forEach((value,index) => {
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+":"+index+"' | this role id is invalid")
}
})
}else if (mode == "serverid"){
arrayValue.forEach((value,index) => {
if (value.length < 16 || value.length > 40 || !/^\d+$/.test(value)){
createError("'"+path+":"+index+"' | this server id is invalid")
}
})
}
}
/**@param {String} value @param {"string"|"boolean"|"number"|"array"|"object"} type */
const checkType = (value,type,path) => {
if (type == "boolean"){
if (typeof value != "boolean"){
createError("'"+path+"' | invalid type, this must be a boolean!")
return true
}else return false
}else if (type == "number"){
if (typeof value != "number"){
createError("'"+path+"' | invalid type, this must be a number!")
return true
}else return false
}else if (type == "string"){
if (typeof value != "string"){
createError("'"+path+"' | invalid type, this must be a string!")
return true
}else return false
}else if (type == "array"){
if (!Array.isArray(value)){
createError("'"+path+"' | invalid type, this must be an array!")
return true
}else return false
}else if (type == "object"){
if (typeof value != "object"){
createError("'"+path+"' | invalid type, this must be an object!")
return true
}else return false
}else return false
}
const checkButtonColor = (value,path) => {
if (value != "none" && value != "grey" && value != "gray" && value != "black" && value != "red" && value != "green" && value != "blue" && value != "blurple"){
createError("'"+path+"' | invalid color, it must be one of these: none, gray, red, green, blue!")
}
}
const configParser = require("./utils/configParser")
/**
* @param {configParser.OTAllOptions} option
* @param {String} path
*/
const checkOption = (option,path) => {
//id
checkType(option.id,"string",path+"/id")
if (option.id.includes(" ")){
createError("'"+path+"/id' | option id can't contain space characters!")
}
if (option.id.length > 20){
createError("'"+path+"/id' | option id max lenght is 20!")
}
//name
checkType(option.name,"string",path+"/name")
if (option.name.length > 100){
createWarn("'"+path+"/name' | if your name is too long it maybe doesn't look so good!")
}else if (option.name.length < 1){
createError("'"+path+"/name' | there is no name!")
}
//description
checkType(option.description,"string",path+"/description")
//icon
const emojiRegex = /(?:\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])|(?:<a?:[^:]*:[0-9]+>)/gi
if (option.icon.length > 0){
const isEmoji = emojiRegex.test(option.icon)
if (isEmoji == false){
createError("'"+path+"/icon' | invalid emoji or custom discord emoji")
}
}
checkType(option.icon,"string",path+"/icon")
//label
checkType(option.label,"string",path+"/label")
if (option.label.length < 1 && option.icon.length < 1){
createError("'"+path+"' | you need at least one of these: label, icon!")
}
//type
checkType(option.type,"string",path+"/type")
if (option.type != "ticket" && option.type != "website" && option.type != "role"){
createError("'"+path+"/type' | the type must be one of these: ticket, website, role!")
}
/**@type {"ticket"|"website"|"role"} */
const type = option.type
if (type == "ticket"){
//color
checkType(option.color,"string",path+"/color")
checkButtonColor(option.color,path+"/color")
//adminroles
checkType(option.adminroles,"array",path+"/adminroles")
checkDiscordArray("roleid",option.adminroles,path+"/adminroles")
//channelprefix
checkType(option.channelprefix,"string",path+"/channelprefix")
if (option.channelprefix.includes(" ")){
createWarn("'"+path+"/channelprefix' | this prefix can't contain spaces!")
}
//category
checkType(option.category,"string",path+"/category")
if (option.category.length > 0){
checkDiscord("categoryid",option.category,path+"/category")
}
//message
checkType(option.message,"string",path+"/message")
//enableDmOnOpen
checkType(option.enableDmOnOpen,"boolean",path+"/enableDmOnOpen")
//ticketmessage
checkType(option.ticketmessage,"string",path+"/ticketmessage")
//thumbnail
if (option.thumbnail){
checkType(option.thumbnail.enable,"boolean",path+"/thumbnail/enable")
checkType(option.thumbnail.url,"string",path+"/thumbnail/url")
}else{
createError("'"+path+"/thumbnail' | there is no thumbnail object!")
}
//image
if (option.image){
checkType(option.image.enable,"boolean",path+"/image/enable")
checkType(option.image.url,"string",path+"/image/url")
}else{
createError("'"+path+"/image' | there is no image object!")
}
//closedCategory
if (option.closedCategory){
checkType(option.closedCategory.enable,"boolean",path/"/closedCategory/enable")
checkType(option.closedCategory.id,"string",path/"/closedCategory/id")
if (option.closedCategory.enable){
checkDiscord("roleid",option.closedCategory.id,path+"/closedCategory/id")
}
}else{
createError("'"+path+"/closedCategory' | there is no closedCategory object!")
}
//autoclose
if (option.autoclose){
checkType(option.autoclose.enable,"boolean",path/"/autoclose/enable")
checkType(option.autoclose.inactiveHours,"number",path/"/autoclose/id")
}else{
createError("'"+path+"/autoclose' | there is no autoclose object!")
}
}else if (type == "website"){
//url
checkType(option.url,"string",path+"/url")
if (option.url.includes(" ")){
createWarn("'"+path+"/url' | a url can't contain spaces")
}
if (option.url.length < 1){
createError("'"+path+"/url' | there is no url!")
}
if (!option.url.startsWith("https://") && !option.url.startsWith("http://") && !option.url.startsWith("discord://")){
createWarn("'"+path+"/url' | your url doesn't start with https or http!")
}
if (option.url.startsWith("http://")){
createWarn("'"+path+"/url' | your url is not a HTTPS link! discord can block this url!")
}
}else if (type == "role"){
//color
checkType(option.color,"string",path+"/color")
checkButtonColor(option.color,path+"/color")
//roles
checkDiscordArray("roleid",option.roles,path+"/roles")
//mode
checkType(option.mode,"string",path+"/mode")
if (option.mode != "add&remove" && option.mode != "add" && option.mode != "remove"){
createError("'"+path+"/mode' | mode must be one of these: add, remove, add&remove")
}
//enableDmOnOpen
checkType(option.enableDmOnOpen,"boolean",path+"/enableDmOnOpen")
}
}
/**
*
* @param {configParser.OTConfigMessage} input
* @param {String} path
*/
const checkMessage = (input,path) => {
//id
if (!input.id) createError("'"+path+"/id' | this embed doesn't have an id!")
if (input.id.length > 50) createError("'"+path+"/id' | the id can't be longer than 50")
if (input.id.includes(" ") || input.id.includes("\n")) createError("'"+path+"/id' | the id can't contain spaces!")
//name
if (input.name.length < 1){
createWarn("'"+path+"/name' | this embed has no name!")
}else if (input.name.length > 99) {
createError("'"+path+"/name' | the name can't be longer than 100")
}
//description
if (input.description.length < 0){
createWarn("'"+path+"/description' | this embed doesn't have a description!")
}
//dropdown
checkType(input.dropdown,"boolean",path+"/dropdown")
//footer
checkType(input.enableFooter,"boolean",path+"/enableFooter")
if (input.enableFooter){
if (input.footer.length < 1) createError("'"+path+"/footer' | no footer!")
if (input.footer.length > 200) createError("'"+path+"/footer' | footer length can't be more than 200!")
}
//thumbnail
checkType(input.enableThumbnail,"boolean",path+"/enableThumbnail")
if (input.enableThumbnail){
if (input.thumbnail.length < 1) createError("'"+path+"/thumbnail' | no thumbnail url!")
}
//customColor
checkType(input.enableCustomColor,"boolean",path+"/enableCustomColor")
if (input.enableCustomColor){
if (input.color.length < 1) createError("'"+path+"/color' | no color!")
checkHexColor(input.color,path+"/color")
}
//OPTIONS!!!
if (checkType(input.options,"array",path+"/options")){
var counter = 0
input.options.forEach((option) => {if (!configParser.optionExists(option)){counter++}})
if (counter == input.options.length){
createWarn("'"+path+"/options' | insert the option ids here!")
}else{
input.options.forEach((option,index) => {
if (!configParser.optionExists(option)){
createWarn("'"+path+"/options:"+index+"' | this option doesnt exist!")
}
})
}
}
//other
//ticket explaination & max tickets warning
if (typeof input.other != "object"){
createError("'"+path+"/other' | this object doesn't exist!")
}else{
checkType(input.other.enableTicketExplaination,"boolean",path+"/other/enableTicketExplaination")
checkType(input.other.enableMaxTicketsWarning,"boolean",path+"/other/enableMaxTicketsWarning")
//customDropdownPlaceholder
if (typeof input.other.customDropdownPlaceholder != "object"){
createError("'"+path+"/other/customDropdownPlaceholder' | this object doesn't exist!")
}else{
checkType(input.other.customDropdownPlaceholder.enable,"boolean",path+"/other/customDropdownPlaceholder/enable")
checkType(input.other.customDropdownPlaceholder.text,"string",path+"/other/customDropdownPlaceholder/text")
if (input.other.customDropdownPlaceholder.enable && input.other.customDropdownPlaceholder.text.length < 1){createError("'"+path+"/other/customDropdownPlaceholder/text' | there is no placeholder text!")}
}
//customCategoryText
if (typeof input.other.customCategoryText != "object"){
createError("'"+path+"/other/customCategoryText' | this object doesn't exist!")
}else{
checkType(input.other.customCategoryText.enable,"boolean",path+"/other/customCategoryText/enable")
checkType(input.other.customCategoryText.text,"string",path+"/other/customCategoryText/text")
if (input.other.customCategoryText.enable && input.other.customCategoryText.text.length < 1){createError("'"+path+"/other/customCategoryText/text' | there is no category text!")}
}
//embedTitleURL
if (typeof input.other.embedTitleURL != "object"){
createError("'"+path+"/other/embedTitleURL' | this object doesn't exist!")
}else{
checkType(input.other.embedTitleURL.enable,"boolean",path+"/other/embedTitleURL/enable")
checkType(input.other.embedTitleURL.url,"string",path+"/other/embedTitleURL/text")
if (input.other.embedTitleURL.enable && input.other.embedTitleURL.url.length < 1){createError("'"+path+"/other/embedTitleURL/url' | there is no url!")}
}
}
}
//////////////////////////////////////////
//////////////////////////////////////////
/////////!!! CHECKER.JS START !!!/////////
//////////////////////////////////////////
//////////////////////////////////////////
var configArray = ["color","serverId","token","adminRoles","prefix","languageFile","status","system","options","messages"]
configArray.forEach((item) => {
if (config[item] == undefined){
throw new Error("\n\nMAIN ERROR: the item '"+item+"' doesn't exist in config.json")
}
})
checkHexColor(config.color,"main_color")
checkDiscord("serverid",config.serverId,"serverId")
if (!require("./api/api.json").disable.checkerjs.token && !config.token.fromENV) checkToken(config.token.value)
checkType(config.adminRoles,"array","/adminRoles")
checkDiscordArray("roleid",config.adminRoles,"adminRoles")
checkString(config.prefix,1,40,"prefix","prefix")
//languagefile
checkType(config.languageFile,"string","languageFile")
const languageList = [
"custom",
"english",
"dutch",
"romanian",
"german",
"arabic",
"spanish",
"portuguese",
"french",
"italian",
"czech",
"danish",
"russian",
"turkish",
"polish",
"slovenian",
"thai",
"norwegian",
"greek",
"ukrainian",
"hungarian",
"indonesian",
"kurdish",
"persian",
"latvian",
"estonian",
"japanese",
"korean",
"simplifiedchinese",
"traditionalchinese",
"finnish",
"swedish",
"vietnamese"
]
if (!languageList.some((l) => config.languageFile.startsWith(l))){
createError("'languageFile' | invalid language, more info in the wiki")
}
//status:
checkType(config.status.enabled,"boolean","status/enabled")
if (config.status.enabled){
if (config.status.type != "PLAYING" && config.status.type != "LISTENING" && config.status.type != "WATCHING" && config.status.type != "CUSTOM"){
createError("'status/type' | not a valid status type! (LISTENING,WATCHING,PLAYING)")
}
if (config.status.text.length < 1){
createError("'status/text' | there is no status text!")
}
if (config.status.text.length > 50) createError("'status/text' | text too long!")
}
//system:
//max tickets
checkType(config.system.maxAmountOfTickets,"number","system/maxAmountOfTickets")
//enableDMmessages
checkType(config.system.dmMessages,"boolean","system/dmMessages")
//memberRole
checkType(config.system.memberRole,"string","system/memberRole")
if (config.system.memberRole && ![" ","0","false","null","undefined"].includes(config.system.memberRole)){
checkDiscord("roleid",config.system.memberRole,"system/memberRole")
}else{
createWarn("'system/memberRole' | You don't have a member role, but it's recommended!")
}
//closemode
checkType(config.system.closeMode,"string","system/closeMode")
if (!["normal","adminonly"].includes(config.system.closeMode)){
createError("'system/closeMode' | the close mode must be adminonly or normal")
}
checkType(config.system.showSlashcmdsInHelp,"boolean","system/showSlashcmdsInHelp")
checkType(config.system.answerInEphemeralOnOpen,"boolean","system/answerInEphemeralOnOpen")
//options
config.options.forEach((option,index) => {
checkOption(option,"options/"+index)
})
//messages
config.messages.forEach((message,index) => {
checkMessage(message,"messages/"+index)
})
//the end
if (errorList.length > 0 || warnList.length > 0){
console.log("REPORT:\n===========================")
warnList.forEach((w) => {
const splitw = w.split("'")
if (splitw.length > 1){
var splitstring = chalk.yellow(splitw[0])+chalk.blue("'"+splitw[1]+"'")+chalk.yellow(splitw[2])
if (splitw[3]){splitstring = splitstring+chalk.yellow(splitw[3])}
if (splitw[4]){splitstring = splitstring+chalk.yellow(splitw[4])}
if (splitw[5]){splitstring = splitstring+chalk.yellow(splitw[5])}
}else {var splitstring = chalk.yellow(splitw[0])}
console.log(splitstring)
})
errorList.forEach((e) => {
const splite = e.split("'")
if (splite.length > 1){
var splitstring = chalk.red(splite[0])+chalk.blue("'"+splite[1]+"'")+chalk.red(splite[2])
}else {var splitstring = chalk.red(splite[0])}
console.log(splitstring)
})
if (isError){
console.log("===========================")
console.log("=> "+chalk.bgRed("your bot doesn't work if you don't fix the above errors!"))
if (isWarn){
console.log("\n=> "+chalk.bgYellow("if you ignore warns, some things may work differently than expected!"))
}
console.log(chalk.bold.green("\nDOCUMENTATION => https://otdocs.dj-dj.be\nSUPPORT => https://discord.dj-dj.be\n"))
process.exit(0)
}else if (isWarn){
console.log("===========================")
console.log("=> "+chalk.bgYellow("if you ignore warns, some things may work differently than expected!"))
console.log(chalk.bold.green("\nDOCUMENTATION => https://otdocs.dj-dj.be\nSUPPORT => https://discord.dj-dj.be\n"))
}
}
}
-82
View File
@@ -1,82 +0,0 @@
const fs = require("fs")
const location = "./storage/database.json"
/**@returns {[{category:String, key:String, value:String}]}*/
function getData(){
if (fs.existsSync(location)){
return JSON.parse(fs.readFileSync(location).toString())
}else{
fs.writeFileSync(location,"[]")
return []
}
}
/**@param {[{category:String, key:String, value:String}]} data*/
function setData(data){
fs.writeFileSync(location,JSON.stringify(data,null,"\t"))
}
exports.set = (category,key,value) => {
const currentData = getData()
const exists = currentData.find((d) => (d.category == category) && (d.key == key)) ? true : false
var newData = []
if (exists){
currentData.forEach((d,i) => {
if (d.category == category && d.key == key){
newData.push({category,key,value})
}else{
newData.push(d)
}
})
}else{
currentData.push({category,key,value})
newData = currentData
}
setData(newData)
return exists
}
/**
* @param {String} category
* @param {String} key
* @returns {String|false}
*/
exports.get = (category,key) => {
const currentData = getData()
const tempresult = currentData.find((d) => (d.category == category) && (d.key == key))
const result = tempresult ? tempresult.value : false
return result
}
/**
* @param {String} category
* @returns {[{key:String,value:String}]|false}
*/
exports.getCategory = (category) => {
const currentData = getData()
const result = currentData.filter((v) => v.category == category)
return result
}
/**
* @param {String} category
* @param {String} key
* @param {String} value
*/
exports.delete = (category,key) => {
const currentData = getData()
const exists = currentData.find((d) => (d.category == category) && (d.key == key)) ? true : false
var newData = []
if (exists){
currentData.forEach((d,i) => {
if (!(d.category == category && d.key == key)){
newData.push(d)
}
})
}else{
newData = currentData
}
setData(newData)
return exists
}
-87
View File
@@ -1,87 +0,0 @@
const fs = require("fs")
const location = "./storage/stats.json"
/**@returns {[{category:String, key:String, value:String}]}*/
function getData(){
if (fs.existsSync(location)){
return JSON.parse(fs.readFileSync(location).toString())
}else{
fs.writeFileSync(location,"[]")
return []
}
}
/**@param {[{category:String, key:String, value:String}]} data*/
function setData(data){
fs.writeFileSync(location,JSON.stringify(data,null,"\t"))
}
/**
* @param {String} category
* @param {String} key
* @param {String|Boolean|Number} value
* @returns {Boolean} exists?
*/
exports.set = (category,key,value) => {
const currentData = getData()
const exists = currentData.find((d) => (d.category == category) && (d.key == key)) ? true : false
var newData = []
if (exists){
currentData.forEach((d,i) => {
if (d.category == category && d.key == key){
newData.push({category,key,value})
}else{
newData.push(d)
}
})
}else{
currentData.push({category,key,value})
newData = currentData
}
setData(newData)
return exists
}
/**
* @param {String} category
* @param {String} key
* @returns {String|Boolean|Number|undefined}
*/
exports.get = (category,key) => {
const currentData = getData()
const result = currentData.find((d) => (d.category == category) && (d.key == key))
return (result) ? result.value : undefined
}
/**
* @param {String} category
* @returns {[{key:String,value:String|Boolean|Number}]|false}
*/
exports.getCategory = (category) => {
const currentData = getData()
const result = currentData.filter((v) => v.category == category)
return result
}
/**
* @param {String} category
* @param {String} key
* @returns {Boolean} exists?
*/
exports.delete = (category,key) => {
const currentData = getData()
const exists = currentData.find((d) => (d.category == category) && (d.key == key)) ? true : false
var newData = []
if (exists){
currentData.forEach((d,i) => {
if (!(d.category == category && d.key == key)){
newData.push(d)
}
})
}else{
newData = currentData
}
setData(newData)
return exists
}
-23
View File
@@ -1,23 +0,0 @@
/**
==================================================================
ALL DATABASE FILES NEED TO EXPORT THE FOLLOWING FUNCTIONS:
- get(category:String,key:String) => String (value)
- set(category:String,key:String,value:String) => Boolean (success)
- delete(category:String,key:String) => Boolean (success)
- getCategory(category:String) => Array[{key:String,value:String}] (key,value)
==================================================================
*/
try {
var main = require("./databases/main")
var stats = require("./databases/stats")
}catch(err){
console.log(err)
console.log("Unable to find the database handlers!")
process.exit(1)
}
module.exports = {
main,
stats
}
-160
View File
@@ -1,160 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const l = bot.language
const loadChalk = async () => {
return await (await import("chalk")).default
}
const embed = discord.EmbedBuilder
const c = discord.Colors
/**@param {discord.User} user*/
exports.noPermsMessage = (user) => {
return new embed()
.setColor(c.Red)
.setTitle("❌ "+l.errors.noPermsTitle)
.setDescription(l.errors.noPermsDescription)
.setFooter({iconURL:user.displayAvatarURL(),text:user.username})
}
/**@param {discord.User} user*/
exports.noPermsDelete = (user) => {
return new embed()
.setColor(c.Red)
.setTitle("❌ "+l.errors.noPermsTitle)
.setDescription(l.errors.noPermsDelete)
.setFooter({iconURL:user.displayAvatarURL(),text:user.username})
}
/**@param {String} message */
exports.invalidArgsMessage = (message) => {
var x = new embed()
.setColor(c.Red)
.setTitle("❌ "+l.errors.missingArgsTitle)
.setDescription(message)
return x
}
/**@param {String} message */
exports.serverError = (message) => {
var x = new embed()
.setColor(c.Orange)
.setTitle("⚠️ "+l.errors.boterror)
.setDescription(message)
return x
}
/**@param {String[]} list @param {String|undefined} message */
exports.invalidIdChooseFromList = (list,message) => {
const beforemsg = message ? message+"\n\n" : ""
var x = new embed()
.setColor(c.Red)
.setTitle("❌ "+l.errors.chooseFromListTitle)
.setDescription(beforemsg+l.errors.chooseFromListDescription+"\n`"+list.join("`\n`")+"`")
return x
}
exports.failedRenameChannel = new embed()
.setColor(c.Red)
.setTitle("❌ Failed to rename channel")
.setDescription("Discord channels can only be renamed twice every 10 minutes! Open Ticket can't do anything about this error!")
exports.notInATicket = new embed()
.setColor(c.Red)
.setTitle("❌ "+l.errors.notInTicketTitle)
.setDescription(l.errors.notInTicketDescription)
/**@param {String} message @param {String} title */
exports.success = (title,message) => {
var x = new embed()
.setColor(config.color)
.setTitle("✅ "+title)
.setDescription(message)
return x
}
/**@param {String} message @param {String} title */
exports.warning = (title,message) => {
var x = new embed()
.setColor(c.Orange)
.setTitle("⚠️ "+title)
.setDescription(message)
return x
}
/**@param {String} message the message @param {String} title the title @param {String} emoji an discord emoji @param {discord.ColorResolvable} color the embed color*/
exports.custom = (title,message,emoji,color) => {
var x = new embed()
.setColor(color)
.setTitle(emoji+" "+title)
.setDescription(message)
return x
}
exports.clearDebugFile = () => {
const fs = require("fs")
if (!fs.existsSync("./openticketdebug.txt")) return
const content = fs.readFileSync("./openticketdebug.txt").toString().split("\n")
if (content.length < 5000) return
for (let i = 0; i < 4000; i++) {
content.shift()
}
const newContent = "==========================\n<OPEN TICKET DEBUG FILE:>\n=========================="+content.join("\n")
fs.writeFileSync("./openticketdebug.txt",newContent)
}
const normalLog = (debugString) => {
const fs = require("fs")
const content = fs.existsSync("./openticketdebug.txt") ? fs.readFileSync("./openticketdebug.txt").toString() : "==========================\n<OPEN TICKET DEBUG FILE:>\n=========================="
fs.writeFileSync("./openticketdebug.txt",content+"\nSYSTEM: "+debugString)
this.clearDebugFile()
}
/**
*
* @param {"system"|"command"|"info"|"api"|"debug"} type
* @param {String} message
* @param {[{key:String,value:String}]} params
*/
exports.log = async (type,message,params) => {
var ptype = type
const chalk = await loadChalk()
var paramstring = ""
if (params){
if (params.length > 0){
params.forEach((p) => {
paramstring = paramstring+p.key+": "+p.value+" "
})
}
}
const parameters = params ? "("+paramstring+")" : " "
if (ptype == "command"){
console.log(chalk.green("[command] ")+message+" "+chalk.yellow(parameters))
}else if (ptype == "info"){
console.log(chalk.blue("[info] ")+message+" "+chalk.yellow(parameters))
}else if (ptype == "system"){
console.log(chalk.green("[system] ")+message+" "+chalk.yellow(parameters))
}else if (ptype == "api"){
if (require("./api/api.json").enableAPIconsolelogs == true){
console.log(chalk.red("[api v"+require("./api/api.json").version+"] ")+message+" "+chalk.yellow(parameters))
}
ptype = "api v"+require("./api/api.json").version
}else if (ptype == "debug"){
if (!process.argv.some((v) => v == "--debug")) return
console.log(chalk.redBright("[DEBUG] ")+message+" "+chalk.yellow(parameters))
}
const cd = new Date()
const months = ["January","February","March","April","May","June","July","August","September","October","November","December"]
normalLog("["+cd.getDate()+" "+months[cd.getMonth()-1]+" "+cd.getFullYear()+" - "+cd.getSeconds()+":"+cd.getMinutes()+":"+cd.getHours()+"] ["+ptype+"] "+message+" "+parameters)
}
-87
View File
@@ -1,87 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
exports.closeCommandRow = new arb()
.addComponents(
new button()
.setCustomId("OTdeleteTicket1")
.setDisabled(false)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
.addComponents(
new button()
.setCustomId("OTreopenTicket")
.setDisabled(false)
.setStyle(bs.Success)
.setLabel(l.buttons.reopen)
.setEmoji("✔")
)
exports.closeCommandRowDisabled = new arb()
.addComponents(
new button()
.setCustomId("OTdeleteTicket1")
.setDisabled(true)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
.addComponents(
new button()
.setCustomId("OTreopenTicket")
.setDisabled(true)
.setStyle(bs.Success)
.setLabel(l.buttons.reopen)
.setEmoji("✔")
)
exports.openRowNormal = new arb()
.addComponents(
new button()
.setCustomId("OTcloseTicket")
.setDisabled(false)
.setStyle(bs.Secondary)
.setLabel(l.buttons.close)
.setEmoji("🔒")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicket")
.setDisabled(false)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
exports.openRowDisabled = new arb()
.addComponents(
new button()
.setCustomId("OTcloseTicket")
.setDisabled(true)
.setStyle(bs.Secondary)
.setLabel(l.buttons.close)
.setEmoji("🔒")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicket")
.setDisabled(true)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
@@ -1,119 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
exports.firstmsgRowNormal = new arb()
.addComponents(
new button()
.setCustomId("OTclaimTicket")
.setDisabled(false)
.setStyle(bs.Success)
.setLabel("Claim Ticket")
.setEmoji("📌")
)
.addComponents(
new button()
.setCustomId("OTcloseTicket")
.setDisabled(false)
.setStyle(bs.Secondary)
.setLabel(l.buttons.close)
.setEmoji("🔒")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicket")
.setDisabled(false)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
exports.firstmsgRowDisabled = new arb()
.addComponents(
new button()
.setCustomId("OTclaimTicket")
.setDisabled(false)
.setStyle(bs.Success)
.setLabel("Claim Ticket")
.setEmoji("📌")
)
.addComponents(
new button()
.setCustomId("OTcloseTicket")
.setDisabled(true)
.setStyle(bs.Secondary)
.setLabel(l.buttons.close)
.setEmoji("🔒")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicket")
.setDisabled(true)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
exports.firstmsgRowNormalNoClaim = new arb()
.addComponents(
new button()
.setCustomId("OTclaimTicket")
.setDisabled(true)
.setStyle(bs.Secondary)
.setLabel("Claim Ticket")
.setEmoji("📌")
)
.addComponents(
new button()
.setCustomId("OTcloseTicket")
.setDisabled(false)
.setStyle(bs.Secondary)
.setLabel(l.buttons.close)
.setEmoji("🔒")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicket")
.setDisabled(false)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
exports.firstmsgRowDisabledNoClaim = new arb()
.addComponents(
new button()
.setCustomId("OTclaimTicket")
.setDisabled(true)
.setStyle(bs.Secondary)
.setLabel("Claim Ticket")
.setEmoji("📌")
)
.addComponents(
new button()
.setCustomId("OTcloseTicket")
.setDisabled(true)
.setStyle(bs.Secondary)
.setLabel(l.buttons.close)
.setEmoji("🔒")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicket")
.setDisabled(true)
.setStyle(bs.Danger)
.setLabel(l.buttons.delete)
.setEmoji("✖️")
)
@@ -1,71 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
exports.closeVerifyBar = new arb()
.addComponents(
new button()
.setCustomId("OTcloseTicketReason")
.setDisabled(false)
.setStyle(bs.Primary)
.setEmoji("💬")
.setLabel(l.buttons.closeWithReason)
)
.addComponents(
new button()
.setCustomId("OTcloseTicketTrue")
.setDisabled(false)
.setStyle(bs.Secondary)
.setEmoji("✅")
)
.addComponents(
new button()
.setCustomId("OTcloseTicketFalse")
.setDisabled(false)
.setStyle(bs.Secondary)
.setEmoji("❌")
)
exports.deleteVerifyBar = new arb()
.addComponents(
new button()
.setCustomId("OTdeleteTicketTrue")
.setDisabled(false)
.setStyle(bs.Secondary)
.setEmoji("✅")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicketFalse")
.setDisabled(false)
.setStyle(bs.Secondary)
.setEmoji("❌")
)
exports.delete1VerifyBar = new arb()
.addComponents(
new button()
.setCustomId("OTdeleteTicketTrue1")
.setDisabled(false)
.setStyle(bs.Secondary)
.setEmoji("✅")
)
.addComponents(
new button()
.setCustomId("OTdeleteTicketFalse1")
.setDisabled(false)
.setStyle(bs.Secondary)
.setEmoji("❌")
)
-144
View File
@@ -1,144 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
/**
*
* @param {discord.User} addedUser
* @param {discord.User} adder
* @returns {discord.EmbedBuilder}
*/
exports.addEmbed = (addedUser,adder) => {
return new embed()
.setTitle("⬆️ "+l.commands.userAddedTitle.replace("{0}",addedUser.username))
.setColor(mc)
.setFooter({text:adder.username,iconURL:adder.displayAvatarURL()})
.setThumbnail(addedUser.displayAvatarURL({extension:"png"}))
}
/**
*
* @param {discord.User} removedUser
* @param {discord.User} remover
* @returns {discord.EmbedBuilder}
*/
exports.removeEmbed = (removedUser,remover) => {
return new embed()
.setTitle("⬇️ "+l.commands.userRemovedTitle.replace("{0}",removedUser.username))
.setColor(mc)
.setFooter({text:remover.username,iconURL:remover.displayAvatarURL()})
.setThumbnail(removedUser.displayAvatarURL({extension:"png"}))
}
/**
*
* @param {discord.User} closer
* @param {String} description
* @returns {discord.EmbedBuilder}
*/
exports.closeEmbed = (closer,description) => {
const embd = new embed()
.setTitle("🔒 "+l.commands.closeTitle)
.setColor(mc)
.setFooter({text:closer.username,iconURL:closer.displayAvatarURL()})
if (description) embd.setDescription(description)
return embd
}
/**
*
* @param {discord.User} closer
* @param {Number} time
* @returns {discord.EmbedBuilder}
*/
exports.autocloseSignalEmbed = (closer,time) => {
const embd = new embed()
.setTitle("⏰ This ticket got closed by Autoclose!")
.setColor(mc)
.setFooter({text:closer.username,iconURL:closer.displayAvatarURL()})
.setDescription("This ticket has been inactive for `"+time+"h`!")
return embd
}
/**
*
* @param {discord.User} deleter
* @returns {discord.EmbedBuilder}
*/
exports.deleteEmbed = (deleter) => {
return new embed()
.setTitle("❌ "+l.commands.deleteTitle)
.setColor(mc)
.setFooter({text:deleter.username,iconURL:deleter.displayAvatarURL()})
}
/**
*
* @param {discord.User} renamer
* @param {String} newname
* @returns {discord.EmbedBuilder}
*/
exports.renameEmbed = (renamer,newname) => {
return new embed()
.setTitle("🔄 "+l.commands.renameTitle.replace("{0}",newname))
.setColor(mc)
.setFooter({text:renamer.username,iconURL:renamer.displayAvatarURL()})
}
/**
*
* @param {discord.User} reopener
* @returns {discord.EmbedBuilder}
*/
exports.reopenEmbed = (reopener) => {
return new embed()
.setTitle("🔓 "+l.commands.reopenTitle)
.setColor(mc)
.setFooter({text:reopener.username,iconURL:reopener.displayAvatarURL()})
}
/**
*
* @param {discord.User} claimer
* @param {discord.User} user
* @returns {discord.EmbedBuilder}
*/
exports.claimEmbed = (claimer,user) => {
return new embed()
.setTitle("📌 "+l.commands.claimTitle.replace("{0}",claimer.username))
.setColor(mc)
.setFooter({text:user.username,iconURL:user.displayAvatarURL()})
}
/**
*
* @param {discord.User} unclaimer
* @returns {discord.EmbedBuilder}
*/
exports.unclaimEmbed = (unclaimer) => {
return new embed()
.setTitle("🆓 "+l.commands.unclaimTitle)
.setColor(mc)
.setFooter({text:unclaimer.username,iconURL:unclaimer.displayAvatarURL()})
}
/**
*
* @param {discord.User} changer
* @param {String} newtype
* @returns {discord.EmbedBuilder}
*/
exports.changeEmbed = (changer,newtype) => {
return new embed()
.setTitle("🔄 "+l.commands.changeTitle.replace("{0}",newtype))
.setColor(mc)
.setFooter({text:changer.username,iconURL:changer.displayAvatarURL()})
}
@@ -1,60 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const permissionChecker = require("../../../utils/permisssionChecker")
const storage = bot.storage
const embed = discord.EmbedBuilder
const mc = config.color
const permsChecker = require("../../../utils/permisssionChecker")
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
module.exports = () => {
//CLAIM
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTclaimTicket") return
try {
await interaction.deferUpdate()
}catch{}
if (!interaction.guild) return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.editReply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permsChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
permsChecker.sendUserNoPerms(interaction.user)
return
}
hiddendata.push({key:"claimedby",value:interaction.user.id})
bot.hiddenData.writeHiddenData(interaction.channel.id,hiddendata)
storage.set("claimData",interaction.channel.id,interaction.user.id)
interaction.channel.messages.fetchPinned().then(msglist => {
/**@type {discord.Message} */
var firstmsg = msglist.last()
if (firstmsg == undefined || firstmsg.author.id != client.user.id) return interaction.editReply({embeds:[bot.errorLog.notInATicket]})
const newEmbed = new embed(firstmsg.embeds[0].data)
.setFooter({text:"claimed by: "+interaction.user.username,iconURL:interaction.user.displayAvatarURL()})
if (firstmsg.components[0].components[1] && firstmsg.components[0].components[1].disabled){
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabledNoClaim],embeds:[newEmbed]})
}else{
firstmsg.edit({components:[bot.buttons.firstmsg.firstmsgRowNormalNoClaim],embeds:[newEmbed]})
}
interaction.channel.send({embeds:[bot.embeds.commands.claimEmbed(interaction.user,interaction.user)]})
})
})
}
@@ -1,76 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const permissionChecker = require("../../../utils/permisssionChecker")
const storage = bot.storage
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
module.exports = () => {
//CLOSE
var closeTicketButtonChecker = false
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTcloseTicketTrue") return
try {
await interaction.deferUpdate()
} catch{}
if (closeTicketButtonChecker == true) return
closeTicketButtonChecker = true
const firstcomponents = interaction.message.components
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (config.system.closeMode == "adminonly"){
if (!permissionChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
if (!permissionChecker.sendUserNoPerms(interaction.user)){
permissionChecker.sendChannelNoPerms(interaction.channel,interaction.user)
}
interaction.message.edit({components:firstcomponents})
closeTicketButtonChecker = false
return
}
}
if (interaction.message.pinned && interaction.message.author.id == client.user.id){
const claimdata = storage.get("claimData",interaction.channel.id)
if (claimdata && claimdata != "false"){
interaction.message.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabledNoClaim]})
}else{
interaction.message.edit({components:[bot.buttons.firstmsg.firstmsgRowDisabled]})
}
}else{
interaction.message.edit({components:[bot.buttons.close.openRowDisabled]})
}
/**
* @type {String}
*/
const name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
interaction.channel.send({embeds:[bot.embeds.commands.closeEmbed(interaction.user)],components:[bot.buttons.close.closeCommandRow]})
await require("../../../ticketActions/ticketCloser").closeManager(interaction.member,interaction.channel,prefix,"close",false,true)
closeTicketButtonChecker = false
})
}
@@ -1,103 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const permissionChecker = require("../../../utils/permisssionChecker")
const storage = bot.storage
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
const modal = discord.ModalBuilder
const tis = discord.TextInputStyle
const tib = discord.TextInputBuilder
module.exports = () => {
//theModal
const reasonModal = new modal()
.setTitle(l.buttons.closeWithReason)
.setCustomId("OTCloseReasonModal")
const reasonInput = new arb()
.addComponents([
new tib()
.setCustomId('OTreasonInput')
.setLabel(l.messages.modalreason)
.setStyle(tis.Short)
.setMaxLength(100)
.setRequired(true)
.setValue("")
.setPlaceholder("reason")
])
reasonModal.addComponents(reasonInput)
//CLOSE WITH REASON
var closeTicketButtonChecker = false
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTcloseTicketReason") return
if (closeTicketButtonChecker == true) return interaction.deferUpdate()
closeTicketButtonChecker = true
const firstcomponents = interaction.message.components
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (config.system.closeMode == "adminonly"){
if (!permissionChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
if (!permissionChecker.sendUserNoPerms(interaction.user)){
permissionChecker.sendChannelNoPerms(interaction.channel,interaction.user)
}
interaction.message.edit({components:firstcomponents})
closeTicketButtonChecker = false
return interaction.deferUpdate()
}
}
interaction.message.edit({components:[bot.buttons.close.openRowDisabled]})
await interaction.showModal(reasonModal)
})
client.on("interactionCreate",async (interaction) => {
if (interaction.type != discord.InteractionType.ModalSubmit) return
if (interaction.customId != "OTCloseReasonModal") return
interaction.reply({embeds:[bot.embeds.commands.closeEmbed(interaction.user)],components:[bot.buttons.close.closeCommandRow]})
const reason = interaction.fields.getTextInputValue("OTreasonInput")
/**
* @type {String}
*/
const name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
//interaction.channel.send({embeds:[bot.embeds.commands.closeEmbed(interaction.user)],components:[bot.buttons.close.closeCommandRow]})
await require("../../../ticketActions/ticketCloser").closeManager(interaction.member,interaction.channel,prefix,"close",reason,true)
closeTicketButtonChecker = false
})
}
@@ -1,107 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const permissionChecker = require("../../../utils/permisssionChecker")
const storage = bot.storage
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
module.exports = () => {
//DELETE
var deleteTicketButtonChecker = false
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTdeleteTicketTrue") return
await interaction.deferUpdate()
if (deleteTicketButtonChecker == true) return
deleteTicketButtonChecker = true
const firstcomponents = bot.buttons.close.openRowNormal
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permissionChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
if (!permissionChecker.sendUserNoDelete(interaction.user)){
permissionChecker.sendChannelNoDelete(interaction.channel,interaction.user)
}
await interaction.message.edit({components:[firstcomponents]})
deleteTicketButtonChecker = false
return
}
/**
* @type {String}
*/
const name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
await interaction.message.edit({components:[bot.buttons.close.openRowDisabled]})
await interaction.channel.send({embeds:[bot.embeds.commands.deleteEmbed(interaction.user)]})
await require("../../../ticketActions/ticketCloser").closeManager(interaction.member,interaction.channel,prefix,"delete",false,true)
deleteTicketButtonChecker = false
})
//DELETE NO TRANSCRIPT
var deleteTicketButtonChecker1 = false
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTdeleteTicketTrue1") return
await interaction.deferUpdate()
if (deleteTicketButtonChecker1 == true) return
deleteTicketButtonChecker1 = true
const firstcomponents = bot.buttons.close.closeCommandRow
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (!permissionChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
if (!permissionChecker.sendUserNoDelete(interaction.user)){
permissionChecker.sendChannelNoDelete(interaction.channel,interaction.user)
}
await interaction.message.edit({components:[firstcomponents]})
deleteTicketButtonChecker1 = false
return
}
/**
* @type {String}
*/
const name = interaction.channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
await interaction.message.edit({components:[bot.buttons.close.closeCommandRowDisabled]})
await interaction.channel.send({embeds:[bot.embeds.commands.deleteEmbed(interaction.user)]})
await require("../../../ticketActions/ticketCloser").closeManager(interaction.member,interaction.channel,prefix,"delete",false,true)
deleteTicketButtonChecker1 = false
})
}
@@ -1,41 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const permissionChecker = require("../../../utils/permisssionChecker")
const storage = bot.storage
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
module.exports = () => {
//REOPEN
client.on("interactionCreate",async (interaction) => {
if (!interaction.isButton()) return
if (interaction.customId != "OTreopenTicket") return
const hiddendata = bot.hiddenData.readHiddenData(interaction.channel.id)
if (hiddendata.length < 1) return interaction.reply({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
if (config.system.closeMode == "adminonly"){
if (!permissionChecker.ticket(interaction.user.id,interaction.guild.id,ticketId)){
if (!permissionChecker.sendUserNoPerms(interaction.user)){
permissionChecker.sendChannelNoPerms(interaction.channel,interaction.user)
}
return interaction.deferUpdate()
}
}
await interaction.deferUpdate()
interaction.message.edit({embeds:[bot.embeds.commands.reopenEmbed(interaction.user)],components:[bot.buttons.close.openRowNormal]})
require("../../../ticketActions/ticketReopener").reopenTicket(interaction.guild,interaction.channel,interaction.user,ticketId)
})
}
@@ -1,20 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const storage = bot.storage
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
module.exports = () => {
}
@@ -1,11 +0,0 @@
module.exports = () => {
require("./firstmsg")()
require("./verifyBars")()
//accepted
require("./accepted/closing")()
require("./accepted/reopening")()
require("./accepted/deleting")()
require("./accepted/closingReason")()
require("./accepted/claiming")()
}
@@ -1,100 +0,0 @@
const discord = require('discord.js')
const bot = require('../../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const storage = bot.storage
const hiddendata = bot.hiddenData
const embed = discord.EmbedBuilder
const mc = config.color
const button = discord.ButtonBuilder
const arb = discord.ActionRowBuilder
const bs = discord.ButtonStyle
module.exports = () => {
//CLOSE
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTcloseTicket") return
try {
await interaction.deferUpdate()
} catch{}
interaction.message.edit({components:[bot.buttons.verifybars.closeVerifyBar]})
})
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTcloseTicketFalse") return
try {
await interaction.deferUpdate()
} catch{}
if (interaction.message.pinned && interaction.message.author.id == client.user.id){
const claimdata = storage.get("claimData",interaction.channel.id)
if (claimdata && claimdata != "false"){
interaction.message.edit({components:[bot.buttons.firstmsg.firstmsgRowNormalNoClaim]})
}else{
interaction.message.edit({components:[bot.buttons.firstmsg.firstmsgRowNormal]})
}
}else{
interaction.message.edit({components:[bot.buttons.close.openRowNormal]})
}
})
//DELETE
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTdeleteTicket") return
try {
await interaction.deferUpdate()
} catch{}
interaction.message.edit({components:[bot.buttons.verifybars.deleteVerifyBar]})
})
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTdeleteTicketFalse") return
try {
await interaction.deferUpdate()
} catch{}
if (interaction.message.pinned && interaction.message.author.id == client.user.id){
const claimdata = storage.get("claimData",interaction.channel.id)
if (claimdata && claimdata != "false"){
interaction.message.edit({components:[bot.buttons.firstmsg.firstmsgRowNormalNoClaim]})
}else{
interaction.message.edit({components:[bot.buttons.firstmsg.firstmsgRowNormal]})
}
}else{
interaction.message.edit({components:[bot.buttons.close.openRowNormal]})
}
})
//DELETE NO TRANSCRIPT
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTdeleteTicket1") return
try {
await interaction.deferUpdate()
} catch{}
interaction.message.edit({components:[bot.buttons.verifybars.delete1VerifyBar]})
})
client.on("interactionCreate",async interaction => {
if (!interaction.isButton()) return
if (interaction.customId != "OTdeleteTicketFalse1") return
try {
await interaction.deferUpdate()
} catch{}
interaction.message.edit({components:[bot.buttons.close.closeCommandRow]})
})
}
-39
View File
@@ -1,39 +0,0 @@
const index = require("../index")
if (index.developerConfig){
var config = require("../devconfig.json")
}else{var config = require("../config.json")}
var localLanguage = require("../language/english.json")
const fs = require("fs")
const lfexists = fs.existsSync("./language/"+config.languageFile+".json")
if (lfexists) localLanguage = require("../language/"+config.languageFile+".json")
const errorLog = async () => {
const chalk = await (await import("chalk")).default
require("./startscreen").headerDataLanguage("Something went wrong when loading the language!",true)
}
const successLog = async (language) => {
const chalk = await (await import("chalk")).default
require("./startscreen").headerDataLanguage("sucessfully loaded language "+language,false)
index.actionRecorder.push({
category:"ot.managers.language",
file:"./core/languageManager.js",
time:new Date().getTime(),
type:"language.success"
})
}
if (!localLanguage){
const runError = async () => {
await errorLog()
process.exit(1)
}
runError()
}else{
exports.language = localLanguage
successLog(config.languageFile)
}
-7
View File
@@ -1,7 +0,0 @@
██████╗ ██████╗ ███████╗███╗ ██╗ ████████╗██╗ ██████╗██╗ ██╗███████╗████████╗
██╔═══██╗██╔══██╗██╔════╝████╗ ██║ ╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝
██║ ██║██████╔╝█████╗ ██╔██╗ ██║ ██║ ██║██║ █████╔╝ █████╗ ██║
██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║
╚██████╔╝██║ ███████╗██║ ╚████║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝
-104
View File
@@ -1,104 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const storage = bot.storage
const APIEvents = require("./api/modules/events")
module.exports = async () => {
const chalk = await (await import("chalk")).default
client.on("interactionCreate",async (interaction) => {
if (!interaction.isButton() && !interaction.isStringSelectMenu()) return
if (interaction.isButton() && !interaction.customId.startsWith("newR")) return
if (interaction.isStringSelectMenu() && !(interaction.customId == "OTdropdownMenu")) return
bot.actionRecorder.push({
category:"ot.managers.reactionroles",
file:"./core/reactionRoles.js",
time:new Date().getTime(),
type:"reactionRoles.interactionCreated"
})
const optionidRaw = interaction.isStringSelectMenu() ? interaction.values[0] : interaction.customId
if (!optionidRaw.includes("newR")) return
const optionid = optionidRaw.split("newR")[1]
if (!optionid){
interaction.reply({embeds:[bot.errorLog.serverError(l.errors.roleDoesntExist)]})
return
}else{
try {
await interaction.deferUpdate()
} catch{}
}
try {
const option = require("./utils/getButton").getOption(optionid)
/**@type {"add"|"remove"|"add&remove"} */
const mode = option.mode
const user = interaction.member
if (mode == "add"){
bot.actionRecorder.push({
category:"ot.managers.reactionroles",
file:"./core/reactionRoles.js",
time:new Date().getTime(),
type:"reactionRoles.type.add"
})
option.roles.forEach((role) => {
interaction.guild.members.cache.find(u => u.id == user.id).roles.add(role)
log("system","added role (reaction roles)",[{key:"user",value:interaction.user.username},{key:"role",value:role}])
const apirole = interaction.guild.roles.cache.find(r => r.id == role)
APIEvents.onReactionRole("add","add",apirole,interaction.user,interaction.channel,interaction.guild,new Date())
})
}else if (mode == "remove"){
bot.actionRecorder.push({
category:"ot.managers.reactionroles",
file:"./core/reactionRoles.js",
time:new Date().getTime(),
type:"reactionRoles.type.remove"
})
option.roles.forEach((role) => {
interaction.guild.members.cache.find(u => u.id == user.id).roles.remove(role)
log("system","removed role (reaction roles)",[{key:"user",value:interaction.user.username},{key:"role",value:role}])
const apirole = interaction.guild.roles.cache.find(r => r.id == role)
APIEvents.onReactionRole("remove","remove",apirole,interaction.user,interaction.channel,interaction.guild,new Date())
})
}else if (mode == "add&remove"){
bot.actionRecorder.push({
category:"ot.managers.reactionroles",
file:"./core/reactionRoles.js",
time:new Date().getTime(),
type:"reactionRoles.type.add&remove"
})
option.roles.forEach((role) => {
if (!interaction.guild.members.cache.find(u => u.id == user.id).roles.cache.has(role)){
interaction.guild.members.cache.find(u => u.id == user.id).roles.add(role)
log("system","added role (reaction roles)",[{key:"user",value:interaction.user.username},{key:"role",value:role}])
const apirole = interaction.guild.roles.cache.find(r => r.id == role)
APIEvents.onReactionRole("add","add&remove",apirole,interaction.user,interaction.channel,interaction.guild,new Date())
}else {
interaction.guild.members.cache.find(u => u.id == user.id).roles.remove(role)
log("system","removed role (reaction roles)",[{key:"user",value:interaction.user.username},{key:"role",value:role}])
const apirole = interaction.guild.roles.cache.find(r => r.id == role)
APIEvents.onReactionRole("remove","add&remove",apirole,interaction.user,interaction.channel,interaction.guild,new Date())
}
})
}
}catch{
interaction.channel.send({embeds:[bot.errorLog.serverError(l.errors.somethingWentWrong)]})
}
})
}
-87
View File
@@ -1,87 +0,0 @@
const discord = require("discord.js")
const bot = require("../../index")
const client = require("../../index").client
const config = bot.config
const configParser = require("../utils/configParser")
const act = discord.ApplicationCommandType
const acot = discord.ApplicationCommandOptionType
module.exports = async () => {
const sid = config.serverId
const ids = configParser.getTicketValuesArray("id")
/**@type {[{name:String,value:String}]} */
var choices = []
ids.forEach((id) => {
const option = configParser.getTicketById(id)
if (option){
choices.push({name:option.name,value:option.id})
}
})
/**@type {[{name:String,value:String}]} */
var msgchoices = []
config.messages.forEach((msg) => {
msgchoices.push({name:msg.id,value:msg.id})
})
//slashtranslation
const ST = require("../../language/slashcmds/slash.json").data
//create a ticket (/new or /ticket)
client.application.commands.create({
name:"ticket",
description:"Create a ticket!",
descriptionLocalizations:ST.newticket,
defaultPermission:true,
type:act.ChatInput,
options:[
{
type:acot.String,
name:"type",
description:"The type of ticket.",
required:true,
choices:choices
}
]
},sid)
client.application.commands.create({
name:"new",
description:"Create a ticket!",
defaultPermission:true,
type:act.ChatInput,
descriptionLocalizations:ST.newticket,
options:[
{
type:acot.String,
name:"type",
description:"The type of ticket.",
required:true,
choices:choices
}
]
},sid)
//msg
client.application.commands.create({
name:"message",
description:"Create a ticket message.",
defaultPermission:true,
descriptionLocalizations:ST.message,
type:act.ChatInput,
options:[
{
type:acot.String,
name:"id",
description:"The message id.",
required:true,
choices:msgchoices,
max_length:30
}
]
},sid)
}
-23
View File
@@ -1,23 +0,0 @@
const discord = require("discord.js")
const bot = require("../../index")
const client = require("../../index").client
const config = bot.config
module.exports = async () => {
const chalk = (await import("chalk")).default
console.log(chalk.red("\nINSTRUCTIONS:\n\n1: Run the command you want to delete in discord.\n2: You get the message 'This command is now deleted!'\n3: There is also a message in the console that says the command is deleted.\n4: reload your discord."))
client.on("interactionCreate",(interaction) => {
if (!interaction.isChatInputCommand()) return console.log("this isn't a command!")
interaction.reply({content:"This command is now deleted!"})
try {
client.application.commands.delete(interaction.commandId,interaction.guild.id).then(() => {
console.log("deleted the command",chalk.blue(interaction.commandName),"(id:",interaction.commandId+")")
})
}catch{
console.log(chalk.red("something went wrong by the deleting of this command!"))
}
})
}
-305
View File
@@ -1,305 +0,0 @@
const discord = require("discord.js")
const bot = require("../../index")
const client = require("../../index").client
const config = bot.config
const configParser = require("../utils/configParser")
const act = discord.ApplicationCommandType
const acot = discord.ApplicationCommandOptionType
/**@param {Boolean|undefined} invisible*/
module.exports = async (invisible) => {
const chalk = (await import("chalk")).default
const sid = config.serverId
const ids = configParser.getTicketValuesArray("id")
/**@type {[{name:String,value:String}]} */
var choices = []
ids.forEach((id) => {
const option = configParser.getTicketById(id)
if (option){
choices.push({name:option.name,value:option.id})
}
})
/**@type {[{name:String,value:String}]} */
var msgchoices = []
config.messages.forEach((msg) => {
msgchoices.push({name:msg.id,value:msg.id})
})
var readystats = 0
//process.stdout.write("[status] there are "+chalk.blue("0 out of 10")+" commands ready! (this can take up to 40 seconds)")
if (!invisible){
setInterval(() => {
if (process.stdout && process.stdout.cursorTo) process.stdout.cursorTo(0)
process.stdout.write("[status] there are "+chalk.blue(readystats+" out of 14")+" commands ready! (this can take up to 40 seconds)")
if (readystats >= 14){
console.log(chalk.green("\nready!"))
console.log(chalk.blue("you can now start the bot with "+chalk.bgBlue("'npm start'")+"!"))
process.exit(1)
}
},100)
}
const fs = require("fs")
fs.writeFileSync("./storage/slashcmdEnabled.txt",require("../../package.json").version)
//slashtranslation
const ST = require("../../language/slashcmds/slash.json").data
//create a ticket (/new or /ticket)
client.application.commands.create({
name:"ticket",
description:"Create a ticket!",
descriptionLocalizations:ST.newticket,
defaultPermission:true,
type:act.ChatInput,
options:[
{
type:acot.String,
name:"type",
description:"The type of ticket.",
required:true,
choices:choices
}
]
},sid).then(() => {
readystats++
})
client.application.commands.create({
name:"new",
description:"Create a ticket!",
defaultPermission:true,
type:act.ChatInput,
descriptionLocalizations:ST.newticket,
options:[
{
type:acot.String,
name:"type",
description:"The type of ticket.",
required:true,
choices:choices
}
]
},sid).then(() => {
readystats++
})
//help
client.application.commands.create({
name:"help",
description:"View the available commands.",
defaultPermission:true,
descriptionLocalizations:ST.help,
type:act.ChatInput
},sid).then(() => {
readystats++
})
//close
client.application.commands.create({
name:"close",
description:"Close the current ticket.",
defaultPermission:true,
descriptionLocalizations:ST.close,
type:act.ChatInput,
options:[{
name:"reason",
type:discord.ApplicationCommandOptionType.String,
required:false,
description:"The reason to close this ticket.",
maxLength:100
}]
},sid).then(() => {
readystats++
})
//delete
client.application.commands.create({
name:"delete",
description:"Delete the current ticket.",
defaultPermission:true,
descriptionLocalizations:ST.delete,
type:act.ChatInput
},sid).then(() => {
readystats++
})
//msg
client.application.commands.create({
name:"message",
description:"Create a ticket message.",
defaultPermission:true,
descriptionLocalizations:ST.message,
type:act.ChatInput,
options:[
{
type:acot.String,
name:"id",
description:"The message id.",
required:true,
choices:msgchoices,
max_length:30
}
]
},sid).then(() => {
readystats++
})
//add
client.application.commands.create({
name:"add",
description:"Add people to this ticket.",
defaultPermission:true,
descriptionLocalizations:ST.add,
type:act.ChatInput,
options:[
{
type:acot.User,
name:"user",
description:"The user to add.",
required:true
}
]
},sid).then(() => {
readystats++
})
//remove
client.application.commands.create({
name:"remove",
description:"Remove people from this ticket.",
defaultPermission:true,
descriptionLocalizations:ST.remove,
type:act.ChatInput,
options:[
{
type:acot.User,
name:"user",
description:"The user to remove.",
required:true
}
]
},sid).then(() => {
readystats++
})
//rename
client.application.commands.create({
name:"rename",
description:"Rename this ticket.",
defaultPermission:true,
descriptionLocalizations:ST.rename,
type:act.ChatInput,
options:[
{
type:acot.String,
name:"name",
description:"The new name (without prefix).",
required:true
}
]
},sid).then(() => {
readystats++
})
//reopen
client.application.commands.create({
name:"reopen",
description:"Reopen this ticket.",
defaultPermission:true,
descriptionLocalizations:ST.reopen,
type:act.ChatInput
},sid).then(() => {
readystats++
})
//claim
client.application.commands.create({
name:"claim",
description:"Claim this ticket / claim it for someone else.",
defaultPermission:true,
type:act.ChatInput,
options:[
{
name:"user",
type:acot.User,
required:false,
description:"The user to claim for."
}
]
},sid).then(() => {
readystats++
})
//unclaim
client.application.commands.create({
name:"unclaim",
description:"Unclaim this ticket.",
defaultPermission:true,
type:act.ChatInput
},sid).then(() => {
readystats++
})
//category
client.application.commands.create({
name:"change",
description:"Change ticket type.",
defaultPermission:true,
type:act.ChatInput,
options:[
{
type:acot.String,
name:"type",
description:"The new ticket type.",
required:true,
choices:choices
}
]
},sid).then(() => {
readystats++
})
//stats
client.application.commands.create({
name:"stats",
description:"See bot statistics!",
defaultPermission:true,
type:act.ChatInput,
options:[
{
type:acot.Subcommand,
name:"global",
description:"See the global bot stats!"
},
{
type:acot.Subcommand,
name:"ticket",
description:"See the stats of a ticket (use inside of ticket)!"
},
{
type:acot.Subcommand,
name:"user",
description:"See the stats from a user or yourself!",
options:[
{
type:acot.User,
name:"user",
required:false,
description:"See the stats from a specific user!"
}
]
}
]
},sid).then(() => {
readystats++
})
}
-79
View File
@@ -1,79 +0,0 @@
const discord = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const logo = require("fs").readFileSync("./core/logo.txt").toString()
/**@param {import('chalk').ChalkInstance} chalk */
const showFlags = async (chalk) => {
var isFlag = false
if (process.argv.some((v) => v == "--devconfig")) console.log(chalk.blue("[FLAGS] => used dev config instead of normal config")); isFlag = true
if (process.argv.some((v) => v == "--nochecker")) console.log(chalk.blue("[FLAGS] => disabled checker.js")); isFlag = true
if (process.argv.some((v) => v == "--tsoffline")) console.log(chalk.blue("[FLAGS] => offline check for html transcripts disabled!")); isFlag = true
if (process.argv.some((v) => v == "--debug")) console.log(chalk.blue("[FLAGS] => enabled DEBUG mode")); isFlag = true
if (process.argv.some((v) => v == "--noslash")) console.log(chalk.blue("[FLAGS] => slash commands disabled")); isFlag = true
if (process.argv.some((v) => v == "--localstatus")) console.log(chalk.blue("[FLAGS] => using local livestatus.json")); isFlag = true
if (process.argv.some((v) => v == "--noerrorupload")) console.log(chalk.blue("[FLAGS] => not uploading errors to liveStatus")); isFlag = true
if (!isFlag) console.log(chalk.blue("no flags!"))
}
exports.run = async () => {
const chalk = await (await import("chalk")).default
console.log(chalk.hex("f8ba00")(logo))
const version = require("../package.json").version
var headertext = "v"+version+" - Support: https://discord.dj-dj.be - Language: "+config.languageFile
const spaceamount = (84-headertext.length)/2
var i = 0
while (i < spaceamount){
headertext = " "+headertext
i++
}
console.log(chalk.bold(headertext+"\n"))
console.log(chalk.bold(chalk.underline("FLAGS:")))
showFlags(chalk)
}
/**
* @param {import('chalk').ChalkInstance} chalk
* @param {{type:String,text:String,enabled:Boolean}} status
* @param {Boolean} updatingslash
* @param {Boolean} slashmode
*
*/
exports.headerDataReady = (chalk,status,updatingslash,slashmode) => {
console.log("\n"+chalk.bold(chalk.underline("STARTUP INFO:")))
if (status.enabled) console.log(chalk.hex("f8ba00")("status: ")+chalk.bold(status.type.toLowerCase())+" "+status.text)
if (slashmode){
console.log("\n\n"+chalk.red(chalk.underline("STARTING IN SLASH CMD CONFIGURATION MODE!")))
return
}
console.log(chalk.hex("f8ba00")("updating slash cmds: ")+chalk.bold(updatingslash))
}
var languageMSG = ""
var languageErr = false
/**
* @param {String} message
* @param {Boolean} err
*/
exports.headerDataLanguage = async (message,err) => {
languageMSG = message
languageErr = err
}
/**@param {{total:Number,error:Number,success:Number}} plugins*/
exports.headerDataPlugins = async (plugins) => {
const chalk = await (await import("chalk")).default
const lmsg = languageErr ? chalk.red(languageMSG) : languageMSG
console.log(chalk.hex("f8ba00")("language: ")+lmsg)
console.log(chalk.hex("f8ba00")("plugins loaded: ")+chalk.bold(plugins.total+" total ")+"("+plugins.success+"✅ "+plugins.error+"❌)")
console.log("\n"+chalk.bold(chalk.underline("LIVESTATUS:")))
await require("./utils/liveStatus").run(plugins)
console.log("\n"+chalk.bold(chalk.underline("LOGS:")))
}
-383
View File
@@ -1,383 +0,0 @@
const { EmbedBuilder, Guild, TextChannel, Message, User } = require('discord.js')
const bot = require('../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const permissionChecker = require("./utils/permisssionChecker")
/**@typedef {"TICKETS_CREATED"|"TICKETS_CLOSED"|"TICKETS_DELETED"|"TICKETS_REOPENED"|"TICKETS_AUTOCLOSED"|"TRANSCRIPTS_CREATED"|"TIME_SINCE_UPDATE"|"LAST_VERSION"} OTGlobalStatsType */
/**@typedef {"CREATED_AT"|"CREATED_BY"|"STATUS"} OTTicketStatsType */
/**@typedef {"TICKETS_CREATED"|"TICKETS_CLOSED"|"TICKETS_DELETED"|"TICKETS_REOPENED"} OTUserStatsType */
/**
* @callback OTStatsCallback
* @param {Boolean|Number|String|undefined} current
* @returns {Boolean|Number|String|undefined}
*/
/**
* @param {OTGlobalStatsType} type
* @param {OTStatsCallback} callback
*/
const updateGlobalStats = (type,callback) => {
const currentstat = bot.statsStorage.get("global",type)
const newstat = callback(currentstat)
if (["string","number","boolean"].includes(typeof newstat)){
bot.statsStorage.set("global",type,newstat)
}
}
/**
* @param {OTTicketStatsType} type
* @param {String} id
* @param {OTStatsCallback} callback
*/
const updateTicketStats = (type,id,callback) => {
const currentstat = bot.statsStorage.get("ticket"+id,type)
const newstat = callback(currentstat)
if (["string","number","boolean"].includes(typeof newstat)){
bot.statsStorage.set("ticket"+id,type,newstat)
}
}
/**
* @param {OTUserStatsType} type
* @param {String} id
* @param {OTStatsCallback} callback
*/
const updateUserStats = (type,id,callback) => {
const currentstat = bot.statsStorage.get("user"+id,type)
const newstat = callback(currentstat)
if (["string","number","boolean"].includes(typeof newstat)){
bot.statsStorage.set("user"+id,type,newstat)
}
}
/**
*
* @param {"global"|"ticket"|"user"} scope
* @param {OTGlobalStatsType|OTTicketStatsType|OTUserStatsType} type
* @param {String} [id]
*/
const removeStats = (scope,type,id) => {
const newScope = (scope == "global") ? scope : scope+id
bot.statsStorage.delete(newScope,type)
}
/**
*
* @param {"global"|"ticket"|"user"} scope
* @param {OTGlobalStatsType|OTTicketStatsType|OTUserStatsType} type
* @param {String} [id]
*/
const existStats = (scope,type,id) => {
const newScope = (scope == "global") ? scope : scope+id
return (typeof bot.statsStorage.get(newScope,type) == "undefined") ? false : true
}
/**
*
* @param {"global"|"ticket"|"user"} scope
* @param {OTGlobalStatsType|OTTicketStatsType|OTUserStatsType} type
* @param {String} [id]
*/
const getStats = (scope,type,id) => {
const newScope = (scope == "global") ? scope : scope+id
return bot.statsStorage.get(newScope,type)
}
const startupStatsManager = () => {
//UPDATE LAST UPDATE DATE
if (!existStats("global","TIME_SINCE_UPDATE")){
updateGlobalStats("TIME_SINCE_UPDATE",(current) => {
return new Date().getTime()
})
updateGlobalStats("LAST_VERSION",(current) => {
return (require("../package.json")).version
})
}
if ((require("../package.json")).version != getStats("global","LAST_VERSION")){
updateGlobalStats("TIME_SINCE_UPDATE",(current) => {
return new Date().getTime()
})
updateGlobalStats("LAST_VERSION",(current) => {
return (require("../package.json")).version
})
}
//UPDATE THE OTHER STATS
if (!existStats("global","TICKETS_CREATED")){
updateGlobalStats("TICKETS_CREATED",(current) => {
return 0
})
}
if (!existStats("global","TICKETS_CLOSED")){
updateGlobalStats("TICKETS_CLOSED",(current) => {
return 0
})
}
if (!existStats("global","TICKETS_DELETED")){
updateGlobalStats("TICKETS_DELETED",(current) => {
return 0
})
}
if (!existStats("global","TICKETS_AUTOCLOSED")){
updateGlobalStats("TICKETS_AUTOCLOSED",(current) => {
return 0
})
}
if (!existStats("global","TICKETS_REOPENED")){
updateGlobalStats("TICKETS_REOPENED",(current) => {
return 0
})
}if (!existStats("global","TRANSCRIPTS_CREATED")){
updateGlobalStats("TRANSCRIPTS_CREATED",(current) => {
return 0
})
}
//HANDLE MEMBER LEAVE (clear the database)
client.on("guildMemberRemove",(member) => {
if (existStats("user","TICKETS_CREATED",member.id)){
removeStats("user","TICKETS_CREATED",member.id)
}
if (existStats("user","TICKETS_CLOSED",member.id)){
removeStats("user","TICKETS_CLOSED",member.id)
}
if (existStats("user","TICKETS_DELETED",member.id)){
removeStats("user","TICKETS_DELETED",member.id)
}
if (existStats("user","TICKETS_REOPENED",member.id)){
removeStats("user","TICKETS_REOPENED",member.id)
}
})
}
/**
* @returns {Promise<{startDate:Number, updateDate:Number, version:String, ticketsCreated:Number, ticketsClosed:Number, ticketsDeleted:Number, ticketsReopened:Number, ticketsAutoclosed:Number, transcriptsCreated:Number}>}
*/
const getGlobalStats = async () => {
//live data
const startDate = new Date().getTime() - client.uptime
const version = (require("../package.json")).version
//saved data
const ticketsCreated = getStats("global","TICKETS_CREATED")
const ticketsClosed = getStats("global","TICKETS_CLOSED")
const ticketsDeleted = getStats("global","TICKETS_DELETED")
const ticketsReopened = getStats("global","TICKETS_REOPENED")
const ticketsAutoclosed = getStats("global","TICKETS_AUTOCLOSED")
const transcriptsCreated = getStats("global","TRANSCRIPTS_CREATED")
const timeSinceUpdate = getStats("global","TIME_SINCE_UPDATE")
return {
startDate,
updateDate: (typeof timeSinceUpdate == "undefined") ? new Date().getTime() : timeSinceUpdate,
version,
ticketsCreated: (typeof ticketsCreated == "undefined") ? 0 : ticketsCreated,
ticketsClosed: (typeof ticketsClosed == "undefined") ? 0 : ticketsClosed,
ticketsDeleted: (typeof ticketsDeleted == "undefined") ? 0 : ticketsDeleted,
ticketsReopened: (typeof ticketsReopened == "undefined") ? 0 : ticketsReopened,
ticketsAutoclosed: (typeof ticketsAutoclosed == "undefined") ? 0 : ticketsAutoclosed,
transcriptsCreated: (typeof transcriptsCreated == "undefined") ? 0 : transcriptsCreated,
}
}
/**
*
* @param {discord.TextChannel} channel
* @param {Number} limit
* @returns {Promise<Message[]>}
*/
const getmessages = async (channel,limit) => {
const final = []
var lastId = ""
while (true) {
const options = {limit:100}
if (lastId) options.before = lastId
const messages = await channel.messages.fetch(options)
messages.forEach(msg => {final.push(msg)})
lastId = messages.last().id
if (messages.size != 100 || final >= limit) {
break
}
}
return final
}
/**
* @param {Guild} guild
* @param {String} channelid
* @returns {Promise<{createdAt:Number, createdBy:String, messageAmount:Number, participants:String[], ticketName:String, status:"open"|"closed"|"reopened"|"autoclosed"}>}
*/
const getTicketStats = async (guild,channelid) => {
//live data
/**@type {TextChannel|undefined} */
const channel = guild.channels.cache.find((c) => c.id == channelid)
const messageAmount = (channel) ? (await getmessages(channel,5000)).length : 0
const participants = (channel) ? channel.members.map((m) => "<@"+m.user.id+">") : []
//saved data
const createdAt = getStats("ticket","CREATED_AT",channelid)
const status = getStats("ticket","STATUS",channelid)
const TEMPcreatedBy = getStats("ticket","CREATED_BY",channelid)
const TEMPcreatedByUser = (TEMPcreatedBy) ? guild.members.cache.find((m) => m.id == TEMPcreatedBy) : undefined
const createdBy = (TEMPcreatedByUser) ? "<@"+TEMPcreatedByUser.user.id+">" : "an unknown person 😄"
return {
createdAt: (typeof createdAt == "undefined") ? new Date().getTime() : createdAt,
createdBy: (typeof createdBy == "undefined") ? 0 : createdBy,
messageAmount,
participants:participants.slice(0,50),
ticketName:channel.name,
status: (typeof status == "undefined") ? "open" : status
}
}
/**
* @param {Guild} guild
* @param {User} user
* @param {String} [channelid]
* @returns {Promise<{ticketsCreated:Number, ticketsClosed:Number, ticketsDeleted:Number, ticketsReopened:Number, role:"globaladmin"|"ticketadmin"|"member", userName:String}>}
*/
const getUserStats = async (guild,user,channelid) => {
//live data
const hiddendata = bot.hiddenData.readHiddenData(channelid)
const ticketId = (hiddendata.length < 1) ? false : hiddendata.find(d => d.key == "type").value
const isGlobalAdmin = permissionChecker.global(user.id,guild.id)
const isTicketAdmin = (ticketId) ? permissionChecker.ticket(user.id,guild.id,ticketId) : false
const role = (isGlobalAdmin) ? "globaladmin" : ((isTicketAdmin) ? "ticketadmin" : "member")
//saved data
const ticketsCreated = getStats("user","TICKETS_CREATED",user.id)
const ticketsClosed = getStats("user","TICKETS_CLOSED",user.id)
const ticketsDeleted = getStats("user","TICKETS_DELETED",user.id)
const ticketsReopened = getStats("user","TICKETS_REOPENED",user.id)
return {
ticketsCreated: (typeof ticketsCreated == "undefined") ? 0 : ticketsCreated,
ticketsClosed: (typeof ticketsClosed == "undefined") ? 0 : ticketsClosed,
ticketsDeleted: (typeof ticketsDeleted == "undefined") ? 0 : ticketsDeleted,
ticketsReopened: (typeof ticketsReopened == "undefined") ? 0 : ticketsReopened,
role,
userName:user.username
}
}
const createGlobalStatsEmbed = async () => {
const stats = await getGlobalStats()
const sentences = [
`${l.stats.ticketsCreated}: \`${stats.ticketsCreated}\``,
`${l.stats.ticketsClosed}: \`${stats.ticketsClosed}\``,
`${l.stats.ticketsDeleted}: \`${stats.ticketsDeleted}\``,
`${l.stats.ticketsReopened}: \`${stats.ticketsReopened}\``,
`${l.stats.ticketsAutoclosed}: \`${stats.ticketsAutoclosed}\``,
`${l.stats.startupDate}: <t:${Math.round(stats.startDate/1000)}:f>`,
`${l.stats.latestUpdate}: <t:${Math.round(stats.updateDate/1000)}:D>`
]
const embed = new EmbedBuilder()
.setColor(config.color)
.setTitle(`📊 ${l.stats.globalTitle}!`)
.setDescription(sentences.slice(0,5).join("\n"))
.addFields({
name:`${l.stats.systemTitle}:`,
value:sentences.slice(5,7).join("\n")
})
.setFooter({text:`${l.stats.versionTitle}: v${stats.version}`})
return embed
}
/**
* @param {Guild} guild
* @param {String} channelid
*/
const createTicketStatsEmbed = async (guild,channelid) => {
const stats = await getTicketStats(guild,channelid)
if (stats.status == "open"){
var newStatus = `🟢 \`${l.stats.switches.open}\``
}else if (stats.status == "reopened"){
var newStatus = `🟢 \`${l.stats.switches.reopened}\``
}else if (stats.status == "closed"){
var newStatus = `🔴 \`${l.stats.switches.closed}\``
}else if (stats.status == "autoclosed"){
var newStatus = `🔴 \`${l.stats.switches.autoclosed}\``
}
const sentences = [
`${l.stats.ticketCreatedOn}: <t:${Math.round(stats.createdAt/1000)}:f>`,
`${l.stats.ticketCreatedBy}: ${stats.createdBy}`,
`${l.stats.messagesSent}: \`${stats.messageAmount}\``,
`${l.stats.status}: ${newStatus}`
]
const embed = new EmbedBuilder()
.setColor(config.color)
.setTitle(`📊 ${l.stats.ticketTitle}`.replace("{0}",`#${stats.ticketName}!`))
.setDescription(sentences.join("\n"))
.addFields({
name:`${l.stats.participants}:`,
value:stats.participants.join("\n")
})
return embed
}
/**
* @param {Guild} guild
* @param {User} user
* @param {String} channelid
*/
const createUserStatsEmbed = async (guild,user,channelid) => {
const stats = await getUserStats(guild,user,channelid)
if (stats.role == "globaladmin"){
var newRole = `🟢 \`${l.stats.switches.globalAdmin}\``
}else if (stats.role == "ticketadmin"){
var newRole = `🟡 \`${l.stats.switches.ticketAdmin}\``
}else if (stats.role == "member"){
var newRole = `🔴 \`${l.stats.switches.member}\``
}
const sentences = [
`${l.stats.role}: ${newRole}`,
`${l.stats.ticketsCreated}: \`${stats.ticketsCreated}\``,
`${l.stats.ticketsClosed}: \`${stats.ticketsClosed}\``,
`${l.stats.ticketsDeleted}: \`${stats.ticketsDeleted}\``,
`${l.stats.ticketsReopened}: \`${stats.ticketsReopened}\``
]
const embed = new EmbedBuilder()
.setColor(config.color)
.setTitle(`📊 ${l.stats.userTitle}`.replace("{0}",`${stats.userName}!`))
.setDescription(sentences.join("\n"))
return embed
}
module.exports = {
updateGlobalStats,
updateTicketStats,
updateUserStats,
removeStats,
existStats,
getStats,
startupStatsManager,
getGlobalStats,
getTicketStats,
getUserStats,
createGlobalStatsEmbed,
createTicketStatsEmbed,
createUserStatsEmbed
}
-65
View File
@@ -1,65 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const storage = bot.storage
module.exports = () => {
setInterval(async () => {
try{
const tickets = storage.getCategory("autocloseTickets")
if (!tickets) return
tickets.forEach(async (t) => {
if (t.value == "false" || t.value == 0 || typeof t.value != "number") return
try{
const channel = await client.channels.fetch(t.key,{cache:true})
if (!channel || !channel.isTextBased()) return
const message = (await channel.messages.fetch({limit:10})).first()
if (!message) return
const secondsSLM = Math.round((new Date().getTime() - message.createdTimestamp)/1000)
const hoursSLM = secondsSLM/3600
if (hoursSLM > t.value){
/**@type {discord.Guild|false} */
const guild = client.guilds.cache.find((g) => g.id == channel.guild.id)
if (!guild) return
const clientMember = guild.members.cache.find((m) => m.id == client.user.id)
if (!clientMember) return
var name = channel.name
var prefix = ""
const tickets = config.options
tickets.forEach((ticket) => {
if (name.startsWith(ticket.channelprefix)){
prefix = ticket.channelprefix
}
})
require("./ticketCloser").closeManager(clientMember,channel,prefix,"close","OTautoclose",true)
storage.delete("autocloseTickets",channel.id)
//STATS
bot.statsManager.updateGlobalStats("TICKETS_AUTOCLOSED",(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateTicketStats("STATUS",channel.id,(current) => {
return "autoclosed"
})
channel.send({embeds:[bot.embeds.commands.autocloseSignalEmbed(client.user,t.value)],components:[bot.buttons.close.closeCommandRow]})
}
}catch{
log("system","can't find autoclose channel with ID: "+t.key)
storage.delete("autocloseTickets",t.key)
}
})
}catch{}
},120000)
}
-253
View File
@@ -1,253 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const storage = bot.storage
const permsChecker = require("../utils/permisssionChecker")
const pendingDelete = []
/**
*
* @param {discord.GuildMember} member
* @param {discord.TextChannel} channel
* @param {String} prefix
* @param {"delete"|"close"|"deletenotranscript"} mode
* @param {Boolean} nomessage
* @param {String} reason only when closing, not when deleting!
*/
exports.closeManager = async (member,channel,prefix,mode,reason,nomessage) => {
const guild = channel.guild
const user = member.user
const chalk = await (await import("chalk")).default
const newReason = reason ? reason : l.messages.none
//start code
//FIX CHANNEL COULD BE CLOSED AFTER DELETE:
if (pendingDelete.includes(channel.id)) return false
/**@type {String}*/
const channelname = channel.name
const ticketOpenerChannelName = channelname.split(prefix)[1]
//get user that opened the ticket
var getuserID = storage.get("userFromChannel",channel.id)
try {
var ticketOpener = client.users.cache.find(u => u.id === getuserID)
var isDatabaseError = false
}catch{var isDatabaseError = true}
if (!ticketOpener){
console.log(chalk.red("[database error]"),"something went wrong when getting the data in the database.\nNo panic, this error fixes itself!")
var isDatabaseError = true
}
var enableTranscript = true
var deleteRequired = false
if (mode == "delete" || mode == "deletenotranscript"){
//delete
const hiddendata = bot.hiddenData.readHiddenData(channel.id)
if (hiddendata.length < 1) return channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
const ticketData = require("../utils/configParser").getTicketById(ticketId,true)
//check perms
if (!guild) return
if (!permsChecker.ticket(user.id,guild.id,ticketId)){
permsChecker.sendUserNoPerms(user)
return
}
//start delete proccess
deleteRequired = true
if (!nomessage){
await channel.send({content:"**"+l.messages.gettingdeleted+"**"})
}
log("system","deleted a ticket",[{key:"user",value:user.username},{key:"ticket",value:channel.name}])
pendingDelete.push(channel.id)
if (!isDatabaseError) storage.set("amountOfUserTickets",getuserID,Number(storage.get("amountOfUserTickets",getuserID)) - 1)
storage.delete("userFromChannel",channel.id)
storage.delete("claimData",channel.id)
storage.delete("autocloseTickets",channel.id)
if (Number(storage.get("amountOfUserTickets",getuserID)) < 0) storage.set("amountOfUserTickets",getuserID,0)
require("../api/modules/events").onTicketDelete(user,channel,guild,new Date(),{name:channel.name,status:"deleted",ticketOptions:ticketData})
//STATS
bot.statsManager.updateGlobalStats("TICKETS_DELETED",(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateUserStats("TICKETS_DELETED",user.id,(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.removeStats("ticket","CREATED_BY",channel.id)
bot.statsManager.removeStats("ticket","CREATED_AT",channel.id)
bot.statsManager.removeStats("ticket","STATUS",channel.id)
hiddendata.push({key:"pendingdelete",value:"true"})
bot.hiddenData.writeHiddenData(channel.id,hiddendata)
}else if (mode == "close"){
//close
const hiddendata = bot.hiddenData.readHiddenData(channel.id)
if (hiddendata.length < 1) return channel.send({embeds:[bot.errorLog.notInATicket]})
const ticketId = hiddendata.find(d => d.key == "type").value
const ticketData = require("../utils/configParser").getTicketById(ticketId,true)
//check perms
if (config.system.closeMode == "adminonly"){
if (!guild) return
if (!permsChecker.ticket(user.id,guild.id,ticketId)){
permsChecker.sendUserNoPerms(user)
return
}
}
//set new permissions
var permissionArray = []
const pfb = discord.PermissionFlagsBits
if (!isDatabaseError) permissionArray.push({
id:ticketOpener,
type:"member",
allow:[pfb.ViewChannel],
deny:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages]
})
permissionArray.push({
id:guild.roles.everyone,
type:"role",
deny:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel]
})
//add main adminroles
config.adminRoles.forEach((role,index) => {
try {
const adminrole = guild.roles.cache.find(r => r.id == role)
if (!adminrole) return
permissionArray.push({
id:adminrole,
type:"role",
allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel],
deny:[]
})
}catch{
log("system","invalid role! At 'config.json => main_adminroles:"+index)
}
})
if (!ticketData) return
if (ticketData.closedCategory.enable){
/**@type {discord.CategoryChannel} */
const category = guild.channels.cache.find(c => c.id == ticketData.closedCategory.id && c.type == discord.ChannelType.GuildCategory)
try {
channel.setParent(category)
}catch{
bot.errorLog.log("system","failed to move channel to new category!")
}
}
ticketData.adminroles.forEach((role,index) => {
if (!config.adminRoles.includes(role)){
try {
const adminrole = guild.roles.cache.find(r => r.id == role)
if (!adminrole) return
permissionArray.push({
id:adminrole,
type:"role",
allow:[pfb.AddReactions,pfb.ViewChannel,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages],
deny:[]
})
}catch{
log("system","invalid role! At 'config.json => options/ticket/...:"+index)
}
}
})
channel.permissionOverwrites.set(permissionArray)
if (!nomessage){
//message
const embed = new discord.EmbedBuilder()
.setColor(config.color)
.setTitle(":lock: "+l.commands.closeTitle+" :lock:")
.setDescription(l.messages.closedDescription)
channel.send({embeds:[embed],components:[bot.buttons.close.closeCommandRow]})
}
log("system","closed a ticket",[{key:"user",value:user.username},{key:"ticket",value:channel.name}])
//send api event
require("../api/modules/events").onTicketClose(user,channel,guild,new Date(),{name:channel.name,status:"closed",ticketOptions:ticketData},newReason)
//STATS
bot.statsManager.updateGlobalStats("TICKETS_CLOSED",(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateUserStats("TICKETS_CLOSED",user.id,(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateTicketStats("STATUS",channel.id,(current) => {
return "closed"
})
}
/**
*
* @param {discord.TextChannel} channel
* @param {Number} limit
* @returns {Promise<discord.Message[]>}
*/
const getmessages = async (channel,limit) => {
const final = []
var lastId = ""
while (true) {
const options = {limit:100}
if (lastId) options.before = lastId
const messages = await channel.messages.fetch(options)
messages.forEach(msg => {final.push(msg)})
lastId = messages.last().id
if (messages.size != 100 || final >= limit) {
break
}
}
return final
}
const transcriptHandler = async () => {
if (!bot.tsconfig.sendTranscripts.enableChannel && !bot.tsconfig.sendTranscripts.enableDM) return false
const APIEvents = require("../api/modules/events")
const messages = await getmessages(channel,5000)
await require("../transcriptSystem/manager")(messages,guild,channel,user,reason)
APIEvents.onTranscriptCreation(messages,channel,guild,new Date())
}
const deleteHandler = async () => {
if (deleteRequired){
pendingDelete.shift()
//delete HIDDENDATA
storage.delete("HIDDENDATA",channel.id)
await channel.delete()
}
}
if (mode == "delete" || mode == "deletenotranscript") await transcriptHandler()
deleteHandler()
}
-265
View File
@@ -1,265 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const l = bot.language
const log = bot.errorLog.log
const configParser = require("../utils/configParser")
const storage = bot.storage
module.exports = () => {
//dropdown placeholder
client.on("interactionCreate",async (interaction) => {
if (!interaction.isStringSelectMenu()) return
if (interaction.customId != "OTdropdownMenu") return
if (interaction.values.includes("OTChooseTicket")){
await interaction.deferUpdate()
}
})
var closeButton = bot.buttons.firstmsg.firstmsgRowNormal
//ticket button click / create ticket
client.on("interactionCreate",async (interaction) => {
if (interaction.isChatInputCommand() && (interaction.commandName == "new" || interaction.commandName == "ticket")){
var customId = "OTnewT"+interaction.options.getString("type")
}else if (interaction.isButton()){
var customId = interaction.customId
}else if (interaction.isStringSelectMenu() && (interaction.customId == "OTdropdownMenu")){
var customId = interaction.values[0]
}else return
if (interaction.isButton()){
if (interaction.customId.startsWith("OTnewT")){
const optionid = interaction.customId.split("OTnewT")[1]
if (!optionid){
await interaction.reply({embeds:[bot.errorLog.serverError(l.errors.ticketDoesntExist)]})
return
}
}
}
if (configParser.getTicketValuesArray("id").includes(customId)){
//ticketoptions from config
const currentTicketOptions = configParser.getTicketById(customId)
if (currentTicketOptions == false) return interaction.reply({embeds:[bot.errorLog.serverError(l.errors.anotherOption)]})
if (interaction.isButton() || interaction.isStringSelectMenu() || interaction.isChatInputCommand()){
try{
await interaction.deferReply({ephemeral:config.system.answerInEphemeralOnOpen})
}catch{}
}
const currentUserTicketAmount = storage.get("amountOfUserTickets",interaction.member.id)
if (config.system.maxAmountOfTickets == 0 || !currentUserTicketAmount || currentUserTicketAmount == "false" || Number(currentUserTicketAmount) < config.system.maxAmountOfTickets){
//update storage
storage.set("amountOfUserTickets",interaction.member.id,Number(currentUserTicketAmount)+1)
var ticketNumber = interaction.member.user.username
//set ticketName
var ticketName = currentTicketOptions.channelprefix+ticketNumber
var logsname = currentTicketOptions.name
//category
if (currentTicketOptions.category.length < 16 || currentTicketOptions.category.length > 20){
var newTicketCategory = null
}else{
var newTicketCategory = currentTicketOptions.category
}
var permissionsArray = []
const pfb = discord.PermissionFlagsBits
const guild = client.guilds.cache.find(g => g.id == interaction.guild.id)
//set @everyone no ticket access
permissionsArray.push({
id:interaction.guild.roles.everyone,
type:"role",
allow:[],
deny:[pfb.ViewChannel]
})
//add the user that created the ticket
permissionsArray.push({
id:interaction.member.user,
type:"member",
allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel,pfb.ReadMessageHistory]
})
//add main adminroles
config.adminRoles.forEach((role,index) => {
try {
const adminrole = guild.roles.cache.find(r => r.id == role)
if (!adminrole) return
permissionsArray.push({
id:adminrole,
type:"role",
allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel,pfb.ReadMessageHistory]
})
}catch{}
})
//add ticket adminroles
/**
* @type {String[]}
*/
const ticketadmin = currentTicketOptions.adminroles
ticketadmin.forEach((role,index) => {
if (!config.adminRoles.includes(role)){
try {
const adminrole = guild.roles.cache.find(r => r.id == role)
if (!adminrole) return
permissionsArray.push({
id:adminrole,
type:"role",
allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel,pfb.ReadMessageHistory]
})
}catch{}
}
})
//add ticket adminroles
if (currentTicketOptions.readonlyAdminroles){
currentTicketOptions.readonlyAdminroles.forEach((role,index) => {
if (!config.adminRoles.includes(role) && !currentTicketOptions.adminroles.includes(role)){
try {
const adminrole = guild.roles.cache.find(r => r.id == role)
if (!adminrole) return
permissionsArray.push({
id:adminrole,
type:"role",
allow:[pfb.AddReactions,pfb.ViewChannel,pfb.ReadMessageHistory],
deny:[pfb.SendMessages,pfb.AttachFiles,pfb.EmbedLinks]
})
}catch{}
}
})
}
//add member role
if (config.system.memberRole && ![" ","0","false","null","undefined"].includes(config.system.memberRole)){
try {
const userrole = guild.roles.cache.find(r => r.id == config.system.memberRole)
if (userrole){
permissionsArray.push({
id:userrole,
type:"role",
deny:[pfb.ViewChannel]
})
}
}catch{}
}
//create the channel
interaction.guild.channels.create({
name:ticketName,
type:discord.ChannelType.GuildText,
parent:newTicketCategory,
reason:"A new ticket is created",
permissionOverwrites:permissionsArray
}).then((ticketChannel) => {
storage.set("userFromChannel",ticketChannel.id,interaction.member.id)
if (currentTicketOptions.autoclose.enable) storage.set("autocloseTickets",ticketChannel.id,currentTicketOptions.autoclose.inactiveHours)
bot.hiddenData.writeHiddenData(ticketChannel.id,[{key:"type",value:currentTicketOptions.id},{key:"openerid",value:interaction.user.id},{key:"createdms",value:new Date().getTime()}])
var ticketEmbed = new discord.EmbedBuilder()
//.setAuthor({name:interaction.user.id})
.setColor(config.color)
.setTitle(currentTicketOptions.name)
//.setFooter({text:"Ticket Type: "+currentTicketOptions.id})
if (currentTicketOptions.autoclose.enable){
const footerText = l.commands.autocloseTitle.replace("{0}",currentTicketOptions.autoclose.inactiveHours+"h")
ticketEmbed.setFooter({text:footerText})
}
if (currentTicketOptions.ticketmessage.length > 0){
ticketEmbed.setDescription(currentTicketOptions.ticketmessage)
}
if (currentTicketOptions.thumbnail.enable) ticketEmbed.setThumbnail(currentTicketOptions.thumbnail.url)
if (currentTicketOptions.image.enable) ticketEmbed.setImage(currentTicketOptions.image.url)
const herePing = currentTicketOptions.ping['@here'] ? " @here" : ""
const everyonePing = currentTicketOptions.ping['@everyone'] ? " @everyone" : ""
const customPing = currentTicketOptions.ping.custom.enable ? " <@&"+currentTicketOptions.ping.custom.roleId+">" : ""
ticketChannel.send({
content:"<@"+interaction.member.id+">"+herePing+everyonePing+customPing,
embeds:[ticketEmbed],
components:[closeButton]
}).then(firstmsg => {
firstmsg.pin()
})
log("system","created new ticket",[{key:"ticket",value:ticketName},{key:"user",value:interaction.user.username}])
require("../api/modules/events").onTicketOpen(interaction.user,ticketChannel,interaction.guild,new Date(),{name:ticketName,status:"open",ticketOptions:currentTicketOptions})
//STATS:
bot.statsManager.updateGlobalStats("TICKETS_CREATED",(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateUserStats("TICKETS_CREATED",interaction.user.id,(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateTicketStats("CREATED_BY",ticketChannel.id,(current) => {
return interaction.user.id
})
bot.statsManager.updateTicketStats("CREATED_AT",ticketChannel.id,(current) => {
return new Date().getTime()
})
bot.statsManager.updateTicketStats("STATUS",ticketChannel.id,(current) => {
return "open"
})
const channelbutton = new discord.ActionRowBuilder()
.addComponents([
new discord.ButtonBuilder()
.setStyle(discord.ButtonStyle.Link)
.setDisabled(false)
.setEmoji("🎫")
.setLabel(l.commands.goToTicket)
.setURL(ticketChannel.url)
])
try{
if (currentTicketOptions.enableDmOnOpen) interaction.member.send({embeds:[bot.errorLog.custom(l.messages.newTicketDmTitle,currentTicketOptions.message,":ticket:",config.color)],components:[channelbutton]})
}
catch{log("system","failed to send DM")}
if (((interaction.isButton() || interaction.isStringSelectMenu()) && config.system.answerInEphemeralOnOpen) || interaction.isChatInputCommand()){
if (interaction.deferred){
interaction.editReply({embeds:[bot.errorLog.success(l.messages.createdTitle,l.messages.createdDescription)],components:[channelbutton]})
}else{
interaction.reply({embeds:[bot.errorLog.success(l.messages.createdTitle,l.messages.createdDescription)],components:[channelbutton]})
}
}
})
}else{
interaction.editReply({embeds:[bot.errorLog.warning(l.errors.maxAmountTitle,l.errors.maxAmountDescription)]})
try {
if (config.system.dmMessages){
interaction.member.send({embeds:[bot.errorLog.warning(l.errors.maxAmountTitle,l.errors.maxAmountDescription)]})
}
}
catch{log("system","can't send DM to member, member doesn't allow dm's")}
}
}
})
}
-77
View File
@@ -1,77 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const storage = bot.storage
const pfb = discord.PermissionFlagsBits
const permsChecker = require("../utils/permisssionChecker")
/**
*
* @param {discord.Guild} guild
* @param {discord.TextBasedChannel} channel
* @param {discord.User} user
* @param {String} ticketId option id
* @returns
*/
const reopenTicket = (guild,channel,user,ticketId) => {
//check perms
if (config.system.closeMode == "adminonly"){
if (!guild) return
if (!permsChecker.ticket(user.id,guild.id,ticketId)){
permsChecker.sendUserNoPerms(user)
return
}
}
log("system","re-opened a ticket",[{key:"ticket",value:channel.name},{key:"user",value:user.username}])
var permissionsArray = []
//set @everyone no ticket access
permissionsArray.push({
id:guild.roles.everyone,
type:"role",
allow:[],
deny:[pfb.ViewChannel]
})
channel.permissionOverwrites.cache.forEach((p) => {
if (p.type == 1){
permissionsArray.push({
id:p.id,
type:"member",
allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel],
deny:[]
})
}else{
if (p.id != guild.roles.everyone){
permissionsArray.push({
id:p.id,
type:"role",
allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel],
deny:[]
})
}
}
})
channel.permissionOverwrites.set(permissionsArray)
require("../api/modules/events").onTicketReopen(user,channel,guild,new Date(),{name:channel.name,status:"reopened",ticketOptions:false})
//STATS
bot.statsManager.updateGlobalStats("TICKETS_REOPENED",(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateUserStats("TICKETS_REOPENED",user.id,(current) => {
if (typeof current != "undefined") return current+1
return 1
})
bot.statsManager.updateTicketStats("STATUS",channel.id,(current) => {
return "reopened"
})
}
exports.reopenTicket = reopenTicket
@@ -1,123 +0,0 @@
const discord = require('discord.js')
const bot = require("../../../index")
const tsconfig = bot.tsconfig
const tsembeds = require("../embeds")
const tsdb = require("../tsdb")
const otversion = require("../../../package.json").version
const version = require("../info.json").version
/**@typedef {{style:{enableCustomBackground:Boolean, backgroundModus:"color"|"image", backgroundData:String, titleColor:String}, bot:{name:String, id:Number, pfp:String}, ticket:{creatorname:String, creatorid:Number, closedtime:Number, openedtime:Number}}} OThtsDataOption */
/**==========================================================
=============================================================
================THIS FILE ISN'T USED ANYMORE!!===============
This file is for html transcripts V1!
Visit compileJsonV2.js for the current version!
=============================================================
===========================================================*/
/**
*
* @param {discord.Guild} guild
* @param {discord.Channel} channel
* @param {discord.User} user
* @param {discord.Message[]} messagesInv
* @param {OThtsDataOption} data
*/
exports.compile = (guild,channel,user,messagesInv,data) => {
const messages = messagesInv.reverse()
var invisibleMessages = 0
const rawfiles = messages.filter((m) => Array.from(m.attachments).length > 0)
var fileAmount = 0
rawfiles.forEach((msg) => {
fileAmount = fileAmount + Array.from(msg.attachments).length
})
const messagesArray = []
messages.forEach((msg) => {
if (msg.content || msg.embeds.length > 0 || msg.attachments.toJSON().length > 0){
const embedArray = []
const fileArray = []
msg.attachments.forEach((file) => {
fileArray.push({
name:file.name,
type:file.contentType,
size:file.size.toString()+" bytes",
url:file.url
})
})
msg.embeds.forEach((embed) => {
embedArray.push({
title:embed.title || false,
description:embed.description ? embed.description : false,
authorimg:((embed.author && embed.author.iconURL) ? embed.author.iconURL : false),
authortext:(embed.author ? embed.author.name : false),
footerimg:((embed.footer && embed.footer.iconURL) ? embed.footer.iconURL : false),
footertext:(embed.footer ? embed.footer.text : false),
color:embed.hexColor,
image:((embed.image && embed.image.url) ? embed.image.url : false),
thumbnail:((embed.thumbnail && embed.thumbnail.url) ? embed.thumbnail.url : false),
url:(embed.url ? embed.url : false)
})
})
const memberColor = msg.member ? msg.member.displayHexColor : "#ffffff"
messagesArray.push({
author:{
name:msg.author.username,
id:Number(msg.author.id),
color:memberColor,
pfp:msg.author.displayAvatarURL()
},
content:msg.content || "",
timestamp:msg.createdTimestamp,
embeds:embedArray,
files:fileArray
})
}else {invisibleMessages++}
})
const result = {
version,otversion,
style:{
backgroundData:data.style.backgroundData || "",
backgroundModus:data.style.backgroundModus || "",
enableCustomBackground:data.style.enableCustomBackground || false,
titleColor:data.style.titleColor || "#F8BA00"
},
bot:{
name:data.bot.name || "Open Ticket",
id:Number(data.bot.id) || 0,
pfp:data.bot.pfp || "transcripts.dj-dj.be/favicon.png"
},
ticket:{
name:channel.name,
id:Number(channel.id),
guildname:guild.name,
guildid:Number(guild.id),
creatorname:data.ticket.creatorname,
creatorid:Number(data.ticket.creatorid),
closedbyname:user.username,
closedbyid:Number(user.id),
closedtime:data.ticket.closedtime,
openedtime:data.ticket.openedtime,
messages:Array.from(messages).length-invisibleMessages,
files:fileAmount
},
messages:messagesArray
}
return result
}
@@ -1,395 +0,0 @@
const discord = require('discord.js')
const bot = require("../../../index")
const tsconfig = bot.tsconfig
const tsembeds = require("../embeds")
const tsdb = require("../tsdb")
const otversion = require("../../../package.json").version
const version = require("../info.json").version
const mentions = require("./mentions").replacementions
/**@typedef {{background:{enableCustomBackground:Boolean, backgroundModus:"color"|"image", backgroundData:String}, header:{enableCustomHeader:Boolean, decoColor:String, backgroundColor:String, textColor:String}, stats:{enableCustomStats:Boolean, backgroundColor:String, keyTextColor:String, valueTextColor:String, hideBackgroundColor:String, hideTextColor:String}, favicon:{enableCustomFavicon:Boolean, imageUrl:String}} OTTranscriptStyle} */
/**@typedef {{name:String, id:String, pfp:String}} OTTranscriptBot */
/**@typedef {{name:String, id:String, guildname:String, guildid:String, guildinvite:String|false, creatorname:String, creatorid:String, closedbyname:String, closedbyid:String, claimedname:String, claimedid:String, creatorpfp:String, closedbypfp:String, claimedpfp:String, closedtime:Number, openedtime:Number, components:{messages:1, files:1, embeds:1, reactions:1, interactions:{dropdowns:1, buttons:1, total:1}, attachments:{invites:1, images:1, gifs:1, stickers:1}}, roleColors:[{id:String,color:String}] }} OTTranscriptTicket */
/**@typedef {{author:{name:String, id:String, color:String, pfp:String, bot:Boolean, verifiedBot:Boolean, system:Boolean}, content:String|false, timestamp:Number, type:"normal", important:Boolean, edited:Boolean, embeds:[{title:String|false, description:String|false, authorimg:String|false, authortext:String|false, footerimg:String|false, footertext:String|false, color:String, image:String|false, thumbnail:String|false, url:String|false, fields:[{name:String,value:String,inline:Boolean}]}], attachments:[{type:"FILE", name:String, fileType:String, size:String, url:String}, {type:"FILE:image", name:String, fileType:String, size:String, url:String}, {type:"URL:invite", guildId:String, guildName:String, guildSize:Number, guildOnline:Number, image:String, url:String}, {type:"URL:gif", url:String}, {type:"BUILDIN:sticker", name:String, url:String}], components:[{type:"buttons", buttons:[{type:"interaction"|"url", label:String|false, icon:String|false, color:"gray"|"green"|"red"|"blue", id:String|false, url:String|false, disabled:Boolean}]}, {type:"dropdown", placeholder:String|false, options:[{label:String|false, icon:String|false, description:String|false, id:String|false}]}, {type:"reactions", reactions:[{amount:1, emoji:String, type:"svg"|"image"|"gif"}]}], reply:{type:"reply"|"command"|false, user:{name:String, id:String, color:String, pfp:String, bot:Boolean, verifiedBot:Boolean, system:Boolean}, replyOptions:{content:String, messageId:String, channelId:String, guildId:String}, commandOptions:{interactionName:String, interactionId:String}}}} OTTranscriptMessage */
/**@typedef {{enabled:Boolean,premiumToken:String, customCredits:{enable:Boolean, replaceText:String, replaceURL:String, enableReportBug:Boolean},customHeaderUrl:{enabled:Boolean, url:String, text:String}, customTranscriptUrl:{enabled:Boolean, name:String}, customFavicon:{enabled:Boolean, image:String},additionalFlags:String[]}} OTTranscriptPremium */
/**@typedef {{version:"2", otversion:String, language:String, style:OTTranscriptStyle, bot:OTTranscriptBot, ticket:OTTranscriptTicket, messages:OTTranscriptMessage[], premium:OTTranscriptPremium}} OTTranscriptCompileInput */
/**@typedef {{style:OTTranscriptStyle,bot:OTTranscriptBot,ticket:OTTranscriptTicket,premium:OTTranscriptPremium}} OTHTMLDataOption */
/**@param {discord.ButtonStyle} buttonstyle */
const buttonStyleToColor = (buttonstyle) => {
if (buttonstyle == discord.ButtonStyle.Primary){
return "blue"
}else if (buttonstyle == discord.ButtonStyle.Secondary){
return "gray"
}else if (buttonstyle == discord.ButtonStyle.Danger){
return "red"
}else if (buttonstyle == discord.ButtonStyle.Success){
return "green"
}else{
return "gray"
}
}
/**
*
* @param {discord.Guild} guild
* @param {discord.Channel} channel
* @param {discord.User} user
* @param {discord.Message[]} messagesInv
* @param {OTHTMLDataOption} data
*/
exports.compile = (guild,channel,user,messagesInv,data) => {
const messages = messagesInv.reverse()
var invisibleMessages = 0
//MESSAGE STATS COUNTS
var messageAmount = messages.length
var fileAmount = 0
var embedAmount = 0
var reactionsAmount = 0
var interactionAmount = 0
messages.forEach((msg) => {
fileAmount = fileAmount + Array.from(msg.attachments).length
embedAmount = embedAmount + msg.embeds.length
msg.reactions.cache.forEach(reaction => {
reactionsAmount = reactionsAmount + reaction.count
})
if (msg.components && msg.components.length > 0){
msg.components.forEach((component) => {
if (component.components.length > 0){
interactionAmount = interactionAmount + component.components.length
}else{
interactionAmount = interactionAmount+1
}
})
}
})
/**@type {OTTranscriptMessage[]} */
const messagesArray = []
messages.forEach((msg) => {
if (msg.content || msg.embeds.length > 0 || msg.attachments.toJSON().length > 0 || msg.components.length > 0 || msg.reactions.cache.size > 0){
const embedArray = []
const attachmentArray = []
const componentsArray = []
msg.attachments.forEach((file) => {
attachmentArray.push({
type:"FILE",
name:file.name,
fileType:file.contentType,
size:Math.round(file.size/1000)+" kb",
url:file.url
})
})
msg.embeds.forEach((embed) => {
embedArray.push({
title:embed.title || false,
description:embed.description ? mentions(embed.description,guild) : false,
authorimg:((embed.author && embed.author.iconURL) ? embed.author.iconURL : false),
authortext:(embed.author ? embed.author.name : false),
footerimg:((embed.footer && embed.footer.iconURL) ? embed.footer.iconURL : false),
footertext:(embed.footer ? embed.footer.text : false),
color:embed.hexColor,
image:((embed.image && embed.image.url) ? embed.image.url : false),
thumbnail:((embed.thumbnail && embed.thumbnail.url) ? embed.thumbnail.url : false),
url:(embed.url ? embed.url : false),
fields:embed.fields
})
})
const tempreactions = []
msg.reactions.cache.forEach((reaction) => {
const count = reaction.count
const emoji = reaction.emoji
if (emoji instanceof discord.GuildEmoji){
//custom emoji
tempreactions.push({
amount:count,
emoji:emoji.imageURL(),
type:(emoji.animated) ? "gif" : "image"
})
}else if (emoji instanceof discord.ReactionEmoji){
//build-in emoji
tempreactions.push({
amount:count,
emoji:emoji.name,
type:"svg"
})
}
})
componentsArray.push({
type: "reactions",
reactions:tempreactions
})
msg.components.forEach((actionrow) => {
var isdropdown = false
var actioncomponents = []
actionrow.components.forEach((comp) => {
if (comp.type == discord.ComponentType.ActionRow || comp.type == discord.ComponentType.TextInput) return
/**
* type: "buttons";
buttons: [{
type: "interaction" | "url";
label: string | false;
icon: string | false;
color: "gray" | "green" | "red" | "blue";
id: string | false;
url: string | false;
disabled: boolean;
}];
}, {
type: "dropdown";
placeholder: string | false;
options: [{
label: string | false;
icon: string | false;
description: string | false;
id: string | false;
}];
},
*/
if (comp.type == discord.ComponentType.Button){
actioncomponents.push({
type: (comp.style == discord.ButtonStyle.Link) ? "url" : "interaction",
label: (comp.label) ? comp.label : false,
icon: (comp.emoji) ? comp.emoji.name : false,
color: buttonStyleToColor(comp.style),
id: comp.customId ? comp.customId : false,
url: (comp.url) ? comp.url : false,
disabled: false
})
}else{
isdropdown = true
var drpdwnoptions = []
/**@type {discord.APISelectMenuOption[]} */
const dropdownoptions = comp.options
dropdownoptions.forEach((opt) => {
drpdwnoptions.push({
label: opt.label,
icon: (opt.emoji) ? opt.emoji : false,
description: (opt.description) ? opt.description : false,
id: "hello world :)"
})
})
componentsArray.push({
type:"dropdown",
placeholder:false,
options:drpdwnoptions
})
}
})
if (!isdropdown){
componentsArray.push({
type:"buttons",
buttons:actioncomponents
})
}
})
const important = msg.guild ? (msg.content.includes("<@"+msg.guild.id+">") || msg.content.includes("<@everyone") || msg.content.includes("@here")): false
const replytype = (msg.interaction) ? "command" : ((msg.reference) ? "reply" : false)
if (replytype == "command"){
const replycolorRAW = msg.guild.members.cache.find((m) => m.id == msg.interaction.user.id)
const replycolor = (replycolorRAW) ? replycolorRAW.displayHexColor.replace("#000000","#ffffff") : "#ffffff"
var reply = {
type:replytype,
commandOptions:{
interactionId:msg.interaction.id,
interactionName:msg.interaction.commandName
},
user:{
name:msg.interaction.user.displayName,
id:msg.interaction.user.id,
color:replycolor,
pfp:msg.interaction.user.displayAvatarURL(),
bot:msg.interaction.user.bot,
system:msg.interaction.user.system,
verifiedBot:msg.interaction.user.flags.has(discord.UserFlags.VerifiedBot)
}
}
}else if (replytype == "reply"){
var replycontentRAW = messages.find((msg2) => msg2.id == msg.reference.messageId)
var replycontent = (replycontentRAW && replycontentRAW.content) ? replycontentRAW.content.slice(0,(60-replycontentRAW.author.displayName.length)) : "deleted this message"
const replycolorRAW = (replycontentRAW) ? msg.guild.members.cache.find((m) => m.id == replycontentRAW.author.id) : false
const replycolor = (replycolorRAW) ? replycolorRAW.displayHexColor.replace("#000000","#ffffff") : "#ffffff"
var user = (replycontentRAW && replycontentRAW.content) ? {
name:(replycontentRAW) ? replycontentRAW.author.displayName : "undefined",
id:(replycontentRAW) ? replycontentRAW.author.id : "undefined",
color:replycolor,
pfp:(replycontentRAW) ? replycontentRAW.author.displayName : "",
bot:(replycontentRAW) ? replycontentRAW.author.bot : false,
system:(replycontentRAW) ? replycontentRAW.author.system : false,
verifiedBot:(replycontentRAW) ? replycontentRAW.author.flags.has(discord.UserFlags.VerifiedBot) : false,
} : {
name:"someone",
id:"undefined",
color:"#ffffff",
pfp:"https://raw.githubusercontent.com/twitter/twemoji/master/assets/72x72/1f5d1.png",
bot:false,
system:false,
verifiedBot:false,
}
var reply = {
type:replytype,
replyOptions:{
content:replycontent,
messageId: msg.reference.messageId,
channelId: msg.reference.channelId,
guildId: msg.reference.guildId
},
user:user
}
}else{var reply = {type:replytype}}
const authorColor = msg.member ? msg.member.displayHexColor.replace("#000000","#ffffff") : "#ffffff"
messagesArray.push({
author:{
name:msg.author.displayName,
id:msg.author.id,
color:authorColor,
pfp:msg.author.displayAvatarURL(),
bot:msg.author.bot,
system:msg.author.system,
verifiedBot:msg.author.flags.has(discord.UserFlags.VerifiedBot)
},
content:mentions(msg.content,guild) || "",
timestamp:msg.createdTimestamp,
embeds:embedArray,
attachments:attachmentArray,
edited: (typeof msg.editedTimestamp == "number"),
type:"normal",
important:important,
reply:reply,
components:componentsArray
})
}else {invisibleMessages++}
})
const dsb = data.style.background
const dsh = data.style.header
const dss = data.style.stats
const dsf = data.style.favicon
/**@type {OTTranscriptCompileInput} */
const result = {
version,otversion,
language:bot.config.languageFile,
style:{
background:{
backgroundData:dsb.backgroundData || "",
backgroundModus:dsb.backgroundModus || "",
enableCustomBackground:dsb.enableCustomBackground || false,
},
header:{
backgroundColor:dsh.backgroundColor || "#202225",
decoColor:dsh.decoColor || "#F8BA00",
textColor:dsh.textColor || "#ffffff",
enableCustomHeader:dsh.enableCustomHeader || false
},
stats:{
backgroundColor:dss.backgroundColor || "#202225",
keyTextColor:dss.keyTextColor || "#737373",
valueTextColor:dss.valueTextColor || "#ffffff",
hideBackgroundColor:dss.hideBackgroundColor || "#40444a",
hideTextColor:dss.hideTextColor || "#ffffff",
enableCustomStats:dss.enableCustomStats || false
}
},
bot:{
name:data.bot.name || "Open Ticket",
id:data.bot.id || 0,
pfp:data.bot.pfp || "transcripts.dj-dj.be/favicon.png"
},
ticket:{
name:channel.name,
id:channel.id,
guildname:guild.name,
guildid:guild.id,
guildinvite:"",
creatorname:data.ticket.creatorname,
creatorid:data.ticket.creatorid,
creatorpfp:data.ticket.creatorpfp,
closedbyname:user.displayName,
closedbyid:user.id,
closedbypfp:user.displayAvatarURL(),
claimedid:"",
claimedname:"",
claimedpfp:"",
closedtime:data.ticket.closedtime,
openedtime:data.ticket.openedtime,
roleColors:[],
components:{
messages:messageAmount,
embeds:embedAmount,
files:fileAmount,
interactions:{
buttons:0,
dropdowns:0,
total:interactionAmount
},
reactions:reactionsAmount,
attachments:{
gifs:0,
images:0,
stickers:0,
invites:0
}
}
},
messages:messagesArray,
premium:{
enabled:false,
premiumToken:"",
customCredits:{
enable:false,
replaceText:"Powered By Open Ticket!",
replaceURL:"https://openticket.dj-dj.be",
enableReportBug:true
},
customHeaderUrl:{
enabled:false,
url:"https://openticket.dj-dj.be",
text:"Hello!"
},
customTranscriptUrl:{
enabled:false,
name:"test-server"
},
customFavicon:{
enabled:dsf.enableCustomFavicon,
image:(dsf.imageUrl) ? dsf.imageUrl : "https://openticket.dj-dj.be"
},
additionalFlags:[]
}
}
return result
}
@@ -1,29 +0,0 @@
const axios = require("axios").default
const discord = require('discord.js')
const bot = require("../../../index")
const tsconfig = bot.tsconfig
const tsembeds = require("../embeds")
const tsdb = require("../tsdb")
/**@typedef {{status:"success"|"error", id:String, time:Number, estimated:{lastdump: Number, processtime:Number,waittime:Number}, transcriptstatus:{value:2, data:{fetchedmsgs:true, uploaded:true, inqueue:true, processed:false, waiting:false, available:false}}}} OTuploadResponse */
/**
* @param {Object} json
* @returns {Promise<OTuploadResponse|false>}
*/
exports.upload = async (json) => {
return new Promise(async (resolve,reject) => {
const data = encodeURIComponent(JSON.stringify(json))
try {
const res = await axios.post("https://apis.dj-dj.be/transcripts/upload?auth=openticketTRANSCRIPT1234&version=2",JSON.stringify(json))
if (res.status != 200) resolve(false)
resolve(res.data)
}catch(err){
console.log("Failed HTML transcript upload! Reached Ratelimit 429!")
resolve(false)
}
})
}
@@ -1,57 +0,0 @@
const discord = require('discord.js')
const bot = require("../../../index")
const tsconfig = bot.tsconfig
const tsembeds = require("../embeds")
const tsdb = require("../tsdb")
/**
*
* @param {String} text
* @param {discord.Guild} guild
*/
exports.replacementions = (text,guild) => {
const userregex = /<@([0-9]+)>(\s)?/g
const usertext = text.replace(userregex,(x) => {
var res = userregex.exec(x)
userregex.lastIndex = 0
var member = guild.members.cache.find((m) => m.id == res[1])
var text = member ? member.user.username.replace(/\s/g,"&nbsp;") : res[1]
return "<@"+text+"> "
})
const channelregex = /<#([0-9]+)>(\s)?/g
const channeltext = usertext.replace(channelregex,(x) => {
var res = channelregex.exec(x)
channelregex.lastIndex = 0
var channel = guild.channels.cache.find((c) => c.id == res[1])
var text = channel ? channel.name.replace(/\s/g,"&nbsp;") : res[1]
return "<#"+text+"> "
})
const roleregex = /<@&([0-9]+)>(\s)?/g
const roletext = channeltext.replace(roleregex,(x) => {
var res = roleregex.exec(x)
roleregex.lastIndex = 0
var role = guild.roles.cache.find((r) => r.id == res[1])
var text = role ? role.name.replace(/\s/g,"&nbsp;") : res[1]
var color = role ? ((role.hexColor == "#000000") ? "regular" : role.hexColor) : "regular"
return "<@&"+text+"::"+color+"> "
})
const defaultroleregex = /@(everyone|here)(\s)?/g
const defaultroletext = roletext.replace(defaultroleregex,(x) => {
var res = defaultroleregex.exec(x)
defaultroleregex.lastIndex = 0
var text = res[1]
return "<@&"+text+"::regular> "
})
return defaultroletext
}
-80
View File
@@ -1,80 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const storage = bot.storage
const tsconfig = bot.tsconfig
/**
*
* @param {String} name
* @param {String} id
* @param {Number} rawprocesstime milliseconds
* @param {discord.User} user
*/
exports.beingprocessed = (name,id,rawprocesstime,user) => {
const newtime = rawprocesstime
const processtime = "<t:"+newtime.getTime().toString().substring(0,newtime.getTime().toString().length-3)+":R>"
const embed = new discord.EmbedBuilder()
.setTitle("🧾 "+l.transcripts.title)
.setColor(config.color)
.setAuthor({name:user.username,iconURL:user.displayAvatarURL()})
.setFooter({text:name})
.setDescription("`"+l.transcripts.processed+"...`\n"+l.transcripts.wait+"\n\n"+l.transcripts.estimated+": "+processtime)
return embed
}
/**
*
* @param {String} name channel name
* @param {String} url transcript url
* @param {discord.User} user transcript closer
* @param {discord.User} creator transcript creator
*/
exports.tsready = (name,url,user,creator) => {
const embed = new discord.EmbedBuilder()
.setTitle("🧾 "+l.transcripts.title)
.setColor(config.color)
.setAuthor({name:user.username,iconURL:user.displayAvatarURL()})
.setFooter({text:name})
if (creator) embed.setFields([{name:"Ticket Creator", value:"<@"+creator.id+">"}])
if (url){
embed.setURL(url)
embed.setDescription("\n["+l.transcripts.available+"]("+url+")\n")
}else{
embed.setDescription("\n"+l.transcripts.available+"\n")
}
return embed
}
/**
*
* @param {String} name
* @param {String} id
* @param {discord.User} user
* @param {undefined|String} err
*/
exports.tserror = (name,id,user,err) => {
const embed = new discord.EmbedBuilder()
.setTitle("❌ "+l.transcripts.title)
.setColor(discord.Colors.Red)
.setAuthor({name:user.username,iconURL:user.displayAvatarURL()})
.setFooter({text:name})
const errDesc = (typeof err == "string") ? "\n"+err : ""
embed.setDescription("Something went wrong while creating the HTML transcript.\n\n[possible reasons](https://otdocs.dj-dj.be/docs/current/docs/system/transcripts)"+errDesc)
return embed
}
-3
View File
@@ -1,3 +0,0 @@
{
"version":"2"
}
-221
View File
@@ -1,221 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const log = bot.errorLog.log
const l = bot.language
const storage = bot.storage
const tsconfig = bot.tsconfig
const tsembeds = require("./embeds")
const tsdb = require("./tsdb")
const checkAvailability = async () => {
if (process.argv.some((v) => v == "--tsoffline")) return true
const axios = require("axios").default
try{
const res = await axios.get("https://apis.dj-dj.be/transcripts/status.json")
if (!res || !(res.status == 200) || !res.data) return false
if (res.data && res.data["v2"] == "online") return true
else return false
}catch{
return false
}
}
/**
*
* @param {discord.Message[]} messages
* @param {discord.Guild} guild
* @param {discord.TextChannel} channel
* @param {discord.User} user
* @param {String|false} reason
*/
module.exports = async (messages,guild,channel,user,reason) => {
require("../api/modules/events").onTranscriptCreation(messages,channel,guild,new Date())
bot.statsManager.updateGlobalStats("TRANSCRIPTS_CREATED",(current) => {
if (typeof current != "undefined") return current+1
return 1
})
const msglist = await channel.messages.fetchPinned()
const chName = channel.name
const chId = channel.id
const asyncmanager = async () => {
const hiddendata = bot.hiddenData.readHiddenData(channel.id)
if (hiddendata.length < 1) return
const ticketId = hiddendata.find(d => d.key == "type").value
const ticketData = require("../utils/configParser").getTicketById(ticketId,true)
const id = hiddendata.find(d => d.key == "openerid").value
const opentime = new Date(Number(hiddendata.find(d => d.key == "createdms").value))
const ticketopener = client.users.cache.find(u => u.id == id)
if (tsconfig.sendTranscripts.useHTMLtranscripts){
const tsb = tsconfig.style.background
const tsh = tsconfig.style.header
const tss = tsconfig.style.stats
const tsf = tsconfig.style.favicon
const JSONDATA = require("./communication/compileJsonV2").compile(guild,channel,user,messages,{
style:{
background:{
enableCustomBackground:tsb.enableCustomBackground,
backgroundModus:tsb.backgroundModus,
backgroundData:tsb.backgroundData
},
header:{
enableCustomHeader:tsh.enableCustomHeader,
backgroundColor:tsh.backgroundColor,
decoColor:tsh.decoColor,
textColor:tsh.textColor
},
stats:{
enableCustomStats:tss.enableCustomStats,
backgroundColor:tss.backgroundColor,
keyTextColor:tss.keyTextColor,
valueTextColor:tss.valueTextColor,
hideBackgroundColor:tss.hideBackgroundColor,
hideTextColor:tss.hideTextColor
},
favicon:{
enableCustomFavicon:tsf.enableCustomFavicon,
imageUrl:tsf.imageUrl
}
},
bot:{
name:client.user.displayName,
id:client.user.id,
pfp:client.user.displayAvatarURL(),
},
ticket:{
creatorid:ticketopener.id,
creatorname:ticketopener.displayName,
creatorpfp:ticketopener.displayAvatarURL(),
openedtime:opentime.getTime(),
closedtime:new Date().getTime()
}
})
if (!await checkAvailability()){
const attachment = await require("./oldTranscript").createTranscript(messages,channel)
const errembed = tsembeds.tserror(chName,chId,user)
if (tsconfig.sendTranscripts.enableChannel){
/**@type {discord.TextChannel|undefined} */
const tc = guild.channels.cache.find((c) => c.id == tsconfig.sendTranscripts.channel)
if (!tc) return
tc.send({embeds:[errembed],files:[attachment]})
}
if (tsconfig.sendTranscripts.enableDM && user){
const embed = tsembeds.tsready(chName,null,user,ticketopener)
try {
ticketopener.send({embeds:[embed],files:[attachment]})
}catch{}
}
return
}
const TSdata = await require("./communication/index").upload(JSONDATA)
if (!TSdata){
const attachment = await require("./oldTranscript").createTranscript(messages,channel)
const errembed = tsembeds.tserror(chName,chId,user,"`HTML Transcript API: reached ratelimit`")
if (tsconfig.sendTranscripts.enableChannel){
/**@type {discord.TextChannel|undefined} */
const tc = guild.channels.cache.find((c) => c.id == tsconfig.sendTranscripts.channel)
if (!tc) return
tc.send({embeds:[errembed],files:[attachment]})
}
if (tsconfig.sendTranscripts.enableDM && user){
const embed = tsembeds.tsready(chName,null,user,ticketopener)
try {
ticketopener.send({embeds:[embed],files:[attachment]})
}catch{}
}
return false
}
if (TSdata.status == "success"){
//MAKE THIS COMPATIBLE WITH PREMIUM "customTranscriptUrl" IN FUTURE VERSIONS!!
const url = "https://transcripts.dj-dj.be/v2/"+TSdata.time+"_"+TSdata.id+".html"
//calculate estimated time
//const lastDumpTime = (new Date(TSdata.estimated.lastdump).getTime())
//const dumpLoopTime = 15000
//const nextDump = new Date(lastDumpTime+dumpLoopTime)
//const processtime = TSdata.estimated.processtime+6000 //6sec extra time for overflow
const duration = TSdata.estimated.waittime
const finaltime = new Date(new Date().getTime()+duration)
//waiting
if (tsconfig.sendTranscripts.enableChannel){
/**@type {discord.TextChannel|undefined} */
const tc = guild.channels.cache.find((c) => c.id == tsconfig.sendTranscripts.channel)
if (!tc) return
const embed = tsembeds.beingprocessed(chName,chId,finaltime,user)
var msg = await tc.send({embeds:[embed]})
}else {var msg = false}
//ready
setTimeout(() => {
if (msg){
msg.edit({embeds:[tsembeds.tsready(chName,url,user,ticketopener)]})
}
if (tsconfig.sendTranscripts.enableDM){
if (!user) return
const embed = tsembeds.tsready(chName,url,user,ticketopener)
try {
ticketopener.send({embeds:[embed]})
}catch{}
}
},duration)
}else{
const attachment = await require("./oldTranscript").createTranscript(messages,channel)
const errembed = tsembeds.tserror(chName,chId,user,"`error type: transcript API error`")
if (tsconfig.sendTranscripts.enableChannel){
/**@type {discord.TextChannel|undefined} */
const tc = guild.channels.cache.find((c) => c.id == tsconfig.sendTranscripts.channel)
if (!tc) return
tc.send({embeds:[errembed],files:[attachment]})
}
if (tsconfig.sendTranscripts.enableDM && user){
const embed = tsembeds.tsready(chName,null,user,ticketopener)
try {
ticketopener.send({embeds:[embed],files:[attachment]})
}catch{}
}
}
}else{
const attachment = await require("./oldTranscript").createTranscript(messages,channel)
const embed = tsembeds.tsready(chName,null,user,ticketopener)
if (tsconfig.sendTranscripts.enableChannel){
/**@type {discord.TextChannel|undefined} */
const tc = guild.channels.cache.find((c) => c.id == tsconfig.sendTranscripts.channel)
if (!tc) return
tc.send({embeds:[embed],files:[attachment]})
}
if (tsconfig.sendTranscripts.enableDM && user){
const embed = tsembeds.tsready(chName,null,user,ticketopener)
try {
ticketopener.send({embeds:[embed],files:[attachment]})
}catch{}
}
}
}
asyncmanager()
}
-60
View File
@@ -1,60 +0,0 @@
const discord = require('discord.js')
const bot = require('../../index')
const client = bot.client
const config = bot.config
const l = bot.language
/**
*
* @param {discord.Message[]} messagecollection
* @param {discord.TextChannel} channel
* @param {Boolean} backup
* @returns new `discord.attachmentBuilder`
*/
exports.createTranscript = async (messagecollection,channel,backup) => {
try {
const cd = new Date()
const months = ["January","February","March","April","May","June","July","August","September","October","November","December"]
const dateString = cd.getDate()+" "+months[cd.getMonth()-1]+" "+cd.getFullYear()+" - "+cd.getHours()+":"+cd.getMinutes()+":"+cd.getSeconds()
let backuptitle = backup ? "BACKUP " : ""
let backupfilename = backup ? "backup" : ""
const filearray = [backuptitle+"TRANSCRIPT:\n"]
messagecollection.reverse()
messagecollection.forEach((msg) => {
const timestamp = new Date(msg.createdTimestamp)
//empty message handling
if (msg.content){
var content = msg.content
}else if (msg.embeds.length > 0 && msg.attachments.length > 0){
var content = "*this message only has embeds & files*"
}else if (msg.embeds.length > 0){
var content = "*this message only has embeds*"
}else if (msg.attachments.length > 0){
var content = "*this message only has files*"
}else{
var content = "*empty message*"
}
const fileurls = []
const rawfiles = Array.from(msg.attachments)
rawfiles.forEach((file) => {
fileurls.push("attachment: "+file[1].url)
})
const filestring = fileurls.length > 0 ? fileurls.join("\n")+"\n" : ""
const msgstats = "files: "+msg.attachments.size+", embeds: "+msg.embeds.length
filearray.push("["+timestamp.getDate()+"/"+timestamp.getMonth()+"/"+timestamp.getFullYear()+", "+timestamp.getHours()+":"+timestamp.getMinutes()+":"+timestamp.getSeconds()+"|"+msg.author.username+"] ["+msgstats+"]\n"+content+"\n"+filestring)
})
if (filearray.length < 2) filearray.push("transcript is empty")
const filestring = filearray.join("\n")
const filebuff = Buffer.from(filestring,"utf-8")
return new discord.AttachmentBuilder(filebuff,{name:backupfilename+"transcript.txt",description:"Open Ticket transcript"})
}catch{
return false
}
}
-71
View File
@@ -1,71 +0,0 @@
const fs = require("fs")
const location = "./storage/tsdatabase.json"
/**@returns {[{category:String, key:String, value:String}]}*/
function getData(){
if (fs.existsSync(location)){
return JSON.parse(fs.readFileSync(location).toString())
}else{
fs.writeFileSync(location,"[]")
return []
}
}
/**@param {[{category:String, key:String, value:String}]} data*/
function setData(data){
fs.writeFileSync(location,JSON.stringify(data,null,"\t"))
}
exports.set = (category,key,value) => {
const currentData = getData()
const exists = currentData.find((d) => (d.category == category) && (d.key == key)) ? true : false
var newData = []
if (exists){
currentData.forEach((d,i) => {
if (d.category == category && d.key == key){
newData.push({category,key,value})
}else{
newData.push(d)
}
})
}else{
currentData.push({category,key,value})
newData = currentData
}
setData(newData)
return exists
}
/**
* @param {String} category
* @param {String} key
* @returns {String|false}
*/
exports.get = (category,key) => {
const currentData = getData()
const tempresult = currentData.find((d) => (d.category == category) && (d.key == key))
const result = tempresult ? tempresult.value : false
return result
}
/**
* @param {String} category
* @param {String} key
* @param {String} value
*/
exports.delete = (category,key) => {
const currentData = getData()
const exists = currentData.find((d) => (d.category == category) && (d.key == key)) ? true : false
var newData = []
if (exists){
currentData.forEach((d,i) => {
if (!(d.category == category && d.key == key)){
newData.push(d)
}
})
}else{
newData = currentData
}
setData(newData)
return exists
}
-188
View File
@@ -1,188 +0,0 @@
const bot = require("../../index")
const config = bot.config
const l = bot.language
//TYPEDEFS:
//==================
//OTTicketOptions
/**@typedef {{id: String,name: String,description: String,icon: String,label: String,type: "ticket"|"role"|"website",color: "red"|"green"|"blue"|"gray",adminroles: String[], readonlyAdminroles: String[], channelprefix: String,category: String,message: String,enableDmOnOpen: Boolean,ticketmessage: String, thumbnail:{enable:Boolean,url:String}, image:{enable:Boolean,url:String}, closedCategory:{enable:Boolean,id:String}, autoclose:{enable:Boolean,inactiveHours:Number}, ping:{"@here":Boolean, "@everyone":Boolean, custom:{enable:Boolean, roleId:String}} }} OTTicketOptions */
//OTRoleOptions
/**@typedef {{id: String,name: String,description: String,icon: String,label: String,type: "ticket"|"role"|"website",color:"red"|"green"|"blue"|"gray"|"none",roles:String[],mode:"add&remove"|"remove"|"add",enableDmOnOpen:Boolean}} OTRoleOptions */
//OTWebsiteOptions
/**@typedef {{id: String,name: String,description: String,icon: String,label: String,type: "ticket"|"role"|"website",url:String}} OTWebsiteOptions */
//OTConfigMessage
/**@typedef {{id: string, name: string, description: string, dropdown: boolean, enableFooter: boolean, footer: string, enableFooterImage: boolean, footerImage: string, enableThumbnail: boolean, thumbnail: string, enableImage: boolean, image: string, enableCustomColor: boolean, color: string, options: string[], other:{enableTicketExplaination: boolean, enableMaxTicketsWarning: boolean, customDropdownPlaceholder:{enable:Boolean,text:String}, customCategoryText:{enable:Boolean,text:String}, embedTitleURL:{enable:Boolean,url:String} } }} OTConfigMessage*/
//OTAllOptions
/**@typedef {{id: String,name: String,description: String,icon: String,label: String,type: "ticket"|"role"|"website",color:"red"|"green"|"blue"|"gray"|"none",roles:String[],mode:"add&remove"|"remove"|"add",adminroles: String[],channelprefix: String,category: String,message: String,enableDmOnOpen: Boolean,ticketmessage: String, thumbnail:{enable:Boolean,url:String}, image:{enable:Boolean,url:String}, url:String, closedCategory:{enable:Boolean,id:String}, autoclose:{enable:Boolean,inactiveHours:Number}, readonlyAdminroles: String[]}} OTAllOptions */
//StringOptions
/**@typedef {"id"|"name"|"description"|"icon"|"label"|"type"|"color"|"adminroles"|"channelprefix"|"category"|"message"|"enableDmOnOpen"|"ticketmessage"|"thumbnail"|"image"|"closedCategory"|"adminroles"|"autoclose"|"ping"} OTTicketStringOptions */
/**@typedef {"id"|"name"|"description"|"icon"|"label"|"type"|"color"|"roles"|"mode"|"enableDmOnOpen"} OTRoleStringOptions */
/**@typedef {"id"|"name"|"description"|"icon"|"label"|"type"|"url"} OTWebsiteStringOptions */
//FUNCTIONS:
//==================
/** @param {String} id the id of the embed @returns {OTConfigMessage}*/
exports.getConfigMessage = (id) => {
var returned = config.messages.find(a => a.id == id)
if (returned){
return returned
}
var result = config.messages[0]
if (!result){
throw new Error("'messages' object in config.json is empty")
return
}
return result
}
/**
*
* @param {String} id
* @param {Boolean} withoutOTnewT
* @returns {OTTicketOptions|false}
*/
exports.getTicketById = (id,withoutOTnewT) => {
var result = false
if (withoutOTnewT){
config.options.forEach((option) => {
if (option.id == id && option.type == "ticket"){
result = option
}
})
}else{
config.options.forEach((option) => {
if ("OTnewT"+option.id == id && option.type == "ticket"){
result = option
}
})
}
return result
}
/**
*
* @param {String} id
* @param {Boolean} withoutOTnewR
* @returns {OTRoleOptions|false}
*/
exports.getRoleById = (id,withoutOTnewR) => {
var result = false
if (withoutOTnewR){
config.options.forEach((option) => {
if (option.id == id && option.type == "role"){
result = option
}
})
}else{
config.options.forEach((option) => {
if ("OTnewR"+option.id == id && option.type == "role"){
result = option
}
})
}
return result
}
/**
*
* @param {String} id
* @returns {OTWebsiteOptions|false}
*/
exports.getWebsiteById = (id) => {
var result = false
config.options.forEach((option) => {
if (option.id == id && option.type == "website"){
result = option
}
})
return result
}
/**
* @param {OTTicketStringOptions} value
* @returns {String[]}
*/
exports.getTicketValuesArray = (value,idWithoutOTnewT) => {
var result = []
const prefix = idWithoutOTnewT ? "" : "OTnewT"
config.options.forEach((opt) => {
if (opt.type == "ticket"){
if (value == "id"){
result.push(prefix+opt[value])
}else{
result.push(opt[value])
}
}
})
return result
}
/**
* @param {OTRoleStringOptions} value
* @returns {String[]}
*/
exports.getRoleValuesArray = (value,idWithoutOTnewR) => {
var result = []
const prefix = idWithoutOTnewR ? "" : "OTnewR"
config.options.forEach((opt) => {
if (o.type == "role"){
if (value == "id"){
result.push(prefix+opt[value])
}else{
result.push(opt[value])
}
}
})
return result
}
/**
* @param {OTWebsiteStringOptions} value
* @returns {String[]}
*/
exports.getWebsiteValuesArray = (value) => {
var result = []
config.options.forEach((opt) => {
if (o.type == "website"){
result.push(opt[value])
}
})
return result
}
/**@type {OTTicketOptions} */
this.ticketType = {}
/**@type {OTRoleOptions} */
this.roleType = {}
/**@type {OTWebsiteOptions} */
this.websiteType = {}
/**@type {OTConfigMessage} */
this.messageType = {}
/**
*
* @param {String} id
* @returns {Boolean}
*/
exports.optionExists = (id) => {
var result = false
config.options.forEach((option) => {
if (option.id == id){
result = true
}
})
return result
}
-80
View File
@@ -1,80 +0,0 @@
const discord = require("discord.js")
const bot = require("../../index")
const config = bot.config
const l = bot.language
const b = discord.ButtonStyle
/**
* @param {String} color
* @returns {"DANGER"|"SUCCESS"|"PRIMARY"|"SECONDARY"} */
const getColor = (color) => {
if (color){
if (color.toLowerCase() == "red"){
return b.Danger
}else if (color.toLowerCase() == "green"){
return b.Success
}else if (color.toLowerCase() == "blue" || color.toLowerCase() == "blurple"){
return b.Primary
}else if (color.toLowerCase() == "black" || color.toLowerCase() == "gray" || color.toLowerCase() == "grey"){
return b.Secondary
}else if (color.toUpperCase() == "DANGER" || color.toUpperCase() == "SECONDARY" || color.toUpperCase() == "SUCCESS" || color.toUpperCase() == "PRIMARY"){
return color
}else if (color.toLowerCase() == "none" || color.toLowerCase() == "false" || color.toLowerCase() == ""){
return b.Secondary
}else return b.Secondary
}else return b.Secondary
}
const getOption = (id) => {
const result = config.options.find((option) => option.id == id)
if (!result){return false}else return result
}
exports.getOption = getOption
/**
*
* @param {String} id
* @returns {discord.ButtonBuilder|false}
*/
exports.getButton = (id) => {
bot.actionRecorder.push({
category:"ot.managers.utils",
file:"./core/utils/getButton.js",
time:new Date().getTime(),
type:"getbutton"
})
const option = getOption(id)
if (!option) return false
if (option.type == "ticket"){
var button = new discord.ButtonBuilder()
.setCustomId("OTnewT"+option.id)
.setDisabled(false)
.setStyle(getColor(option.color))
if (option.label){button.setLabel(option.label)}
if (option.icon){button.setEmoji(option.icon)}
if (!option.label && !option.icon){button.setLabel("no label or emoji")}
}else if (option.type == "website"){
var button = new discord.ButtonBuilder()
.setDisabled(false)
.setStyle(discord.ButtonStyle.Link)
.setURL(option.url)
if (option.label){button.setLabel(option.label)}
if (option.icon){button.setEmoji(option.icon)}
if (!option.label && !option.icon){button.setLabel("no label or emoji")}
}else if (option.type == "role"){
var button = new discord.ButtonBuilder()
.setCustomId("newR"+option.id)
.setDisabled(false)
.setStyle(getColor(option.color))
if (option.label){button.setLabel(option.label)}
if (option.icon){button.setEmoji(option.icon)}
if (!option.label && !option.icon){button.setLabel("no label or emoji")}
}
return button
}
-70
View File
@@ -1,70 +0,0 @@
const discord = require("discord.js")
const bot = require("../../index")
const config = bot.config
const l = bot.language
const configParser = require("./configParser")
/**
*
* @param {String[]} ids
* @param {String|undefined|false} placeholder
* @returns {discord.StringSelectMenuBuilder|false}
*/
exports.getDropdown = (ids,placeholder) => {
bot.actionRecorder.push({
category:"ot.managers.utils",
file:"./core/utils/getDropdown.js",
time:new Date().getTime(),
type:"getdropdown"
})
const dropdown = new discord.StringSelectMenuBuilder()
.setCustomId("OTdropdownMenu")
.setDisabled(false)
.setMaxValues(1)
.setMinValues(1)
.setPlaceholder(placeholder || l.messages.chooseATicket)
.addOptions(
new discord.StringSelectMenuOptionBuilder()
.setDefault(true)
.setLabel(placeholder || l.messages.chooseATicket)
.setValue("OTChooseTicket")
)
ids.forEach((id) => {
const option = configParser.getTicketById(id,true)
const roleoption = configParser.getRoleById(id,true)
if (option){
const selectComponent = new discord.StringSelectMenuOptionBuilder()
.setDefault(false)
.setValue("OTnewT"+option.id)
if (!option.label && !option.icon){
selectComponent.setLabel("OT ERROR: no description/emoji")
}else{
if (option.label) selectComponent.setLabel(option.label)
if (option.icon) selectComponent.setEmoji(option.icon)
if (option.description && option.description.length < 100) selectComponent.setDescription(option.description)
}
dropdown.addOptions(selectComponent)
}else if (roleoption){
const selectComponent = new discord.StringSelectMenuOptionBuilder()
.setDefault(false)
.setValue("OTnewR"+roleoption.id)
if (!roleoption.label && !roleoption.icon){
selectComponent.setLabel("OT ERROR: no description/emoji")
}else{
if (roleoption.label) selectComponent.setLabel(roleoption.label)
if (roleoption.icon) selectComponent.setEmoji(roleoption.icon)
}
dropdown.addOptions(selectComponent)
}
})
return dropdown
}

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