From af8d6351afb30c3586b48674c811f7c08c299ef6 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 11:07:56 +0200 Subject: [PATCH 01/20] 1.4.0 beta --- .DS_Store | Bin 6148 -> 6148 bytes README.md | 26 +- TEMPDOCS.md | 2 + commands/database.js | 21 -- commands/{ticketExtra.js => extracmds.js} | 23 +- commands/help.js | 34 ++ commands/ticket.js | 262 +++------------- commands/ticketSystem.js | 309 ------------------- config.json | 182 +++++------ checker.js => core/checker.js | 6 +- core/closebuttons.js | 206 +++++++++++++ {storage => core/dynamicdatabase}/storage.js | 4 + core/getoptions.js | 113 +++++++ core/slashSystem/slashDisable.js | 23 ++ core/slashSystem/slashEnable.js | 162 ++++++++++ core/ticketCloser.js | 153 +++++++++ core/ticketMessageEmbed.js | 133 ++++++++ core/ticketOpener.js | 171 ++++++++++ core/transcript.js | 36 +++ core/utils/getTicketMessages.js | 13 + documentation/CHANGELOG.md | 96 ------ documentation/CONFIG.md | 68 ---- documentation/Q&A.md | 26 -- index.js | 68 ++-- logo.png | Bin 138198 -> 139078 bytes originalConfig.json | 59 ++++ package.json | 4 +- storage/tickets/779742674932072469 | 1 + storage/tickets/858640346892337163 | 1 + storage/userTickets/968902556971585536 | 1 + storage/userTickets/968902650873675838 | 1 + storage/userTickets/968903429693001779 | 1 + storage/userTickets/968903924390174790 | 1 + storage/userTickets/968913030110904340 | 1 + storage/userTickets/968915615333093387 | 1 + storage/userTickets/968917301288108072 | 1 + storage/userTickets/968920379974373436 | 1 + storage/userTickets/968920740558684210 | 1 + storage/userTickets/968921182877388930 | 1 + storage/userTickets/968921695505223721 | 1 + storage/userTickets/968922637596258304 | 1 + storage/userTickets/968922760787161098 | 1 + storage/userTickets/968923098571239474 | 1 + storage/userTickets/969289034599919666 | 1 + storage/userTickets/969289151692288100 | 1 + storage/userTickets/969289430370230272 | 1 + storage/userTickets/969289687808241746 | 1 + storage/userTickets/969289892662235188 | 1 + storage/userTickets/969290103337938966 | 1 + storage/userTickets/969290783716941914 | 1 + storage/userTickets/970222239867871272 | 1 + storage/userTickets/970581087891886080 | 1 + storage/userTickets/970581271459799040 | 1 + 53 files changed, 1323 insertions(+), 903 deletions(-) create mode 100644 TEMPDOCS.md delete mode 100644 commands/database.js rename commands/{ticketExtra.js => extracmds.js} (81%) create mode 100644 commands/help.js delete mode 100644 commands/ticketSystem.js rename checker.js => core/checker.js (98%) create mode 100644 core/closebuttons.js rename {storage => core/dynamicdatabase}/storage.js (83%) create mode 100644 core/getoptions.js create mode 100644 core/slashSystem/slashDisable.js create mode 100644 core/slashSystem/slashEnable.js create mode 100644 core/ticketCloser.js create mode 100644 core/ticketMessageEmbed.js create mode 100644 core/ticketOpener.js create mode 100644 core/transcript.js create mode 100644 core/utils/getTicketMessages.js delete mode 100644 documentation/CHANGELOG.md delete mode 100644 documentation/CONFIG.md delete mode 100644 documentation/Q&A.md create mode 100644 originalConfig.json create mode 100644 storage/tickets/779742674932072469 create mode 100644 storage/tickets/858640346892337163 create mode 100644 storage/userTickets/968902556971585536 create mode 100644 storage/userTickets/968902650873675838 create mode 100644 storage/userTickets/968903429693001779 create mode 100644 storage/userTickets/968903924390174790 create mode 100644 storage/userTickets/968913030110904340 create mode 100644 storage/userTickets/968915615333093387 create mode 100644 storage/userTickets/968917301288108072 create mode 100644 storage/userTickets/968920379974373436 create mode 100644 storage/userTickets/968920740558684210 create mode 100644 storage/userTickets/968921182877388930 create mode 100644 storage/userTickets/968921695505223721 create mode 100644 storage/userTickets/968922637596258304 create mode 100644 storage/userTickets/968922760787161098 create mode 100644 storage/userTickets/968923098571239474 create mode 100644 storage/userTickets/969289034599919666 create mode 100644 storage/userTickets/969289151692288100 create mode 100644 storage/userTickets/969289430370230272 create mode 100644 storage/userTickets/969289687808241746 create mode 100644 storage/userTickets/969289892662235188 create mode 100644 storage/userTickets/969290103337938966 create mode 100644 storage/userTickets/969290783716941914 create mode 100644 storage/userTickets/970222239867871272 create mode 100644 storage/userTickets/970581087891886080 create mode 100644 storage/userTickets/970581271459799040 diff --git a/.DS_Store b/.DS_Store index 9a1d8ab8356475869d05ad27271af385b44f8717..388f457e989777e5c4312f8b40b47303a55c0dd3 100644 GIT binary patch literal 6148 zcmeHKJ5Iwu5S>jN5%G~yP^2^*0FfFB6NMAx0FcBXAlW#|r^XU@prW9r;shWB2jD13 zNHjFO*daLLb7>{$yS;=}ffr{sd z290(viHEsJafkw@ZXh3H)$$r13`_5i=EKXXjZj^N4!)B01@0I5RcRXA= zK0N)}8_N5yNX0xoN+`y!L+2FIIJ-X4_Q|3cmj~4^H#CpjTyc`+QyiD&MYPMjJk4uF zeV5N|m>rwrabgL>;gr;h+Y4{mL^*0l`ivjZ4dObx;VQzjK?Qk6Yo zD3cDqZ{q@ssX>!YO0SH0?8?fXP)sF--`C-!0)x_v0-}JcK-t{Zxc(m&pa0z?xf2CM zfq$id@>=ay6OUwTYwqE=*7|72Xlxvp8q_K1Y&q5!T#D;x+A!wx1u(Ff8iWTXe+0A) K(uo4Us=y~V^QZm* delta 205 zcmZoMXfc=|#>B)qF;Q%yo+2a1#(>?7j2x4BSR~{3CLCm7U|?a;V@PMnWGKnacX3I| z$xi}`aWF762;bg#*b!Ae1+RQThGB4Wer^F!4+FDB0mI}2tTK}~u=23*+)H2rax~a1 z>WV654^4i@ss&@pv1u{$`fu1Q$RWluv0&R~b`E|Hpksmj@640=MI3<+VPIecI&yP_ H$Qot<;T -[![announcement](https://img.shields.io/badge/new%20announcement!-open--ticket%20v1.4.0-red.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/discussions/3) - -[![q&a](https://img.shields.io/badge/Q&A-click%20here-blue.svg?style=flat-square)](./documentation/Q&A.md) +[![q&a](https://img.shields.io/badge/Q&A-click%20here-blue.svg?style=flat-square)](./TEMPDOCS.md) ## features - discord interaction buttons +- slash & text commands +- close or delete tickets +- translation possible +- dynamic database system - transcripts - add/remove users from ticket - custom colors & name @@ -45,7 +53,7 @@ if there are any errors, you can open an **issue on github** or **contact me in ### config Our config documentation is moved to another file: -[click here to view](./documentation/CONFIG.md) +[click here to view](./TEMPDOCS.md) ### intents & permissions In the discord developer portal in the "bot" panel you will find 3 switches under the title "Gateaway Intents". The following switches should always be turned on @@ -55,14 +63,10 @@ In the discord developer portal in the "bot" panel you will find 3 switches unde To work the best, the bot needs `ADMINISTRATOR` permissions. You can also configure your own permissions -## v1.4.0 announcement -Hello everyone, soon open-ticket v1.4.0 will be launched, it contains a lot of new features. You can view them below. -[click here to view the announcement](https://github.com/DJj123dj/open-ticket/discussions/3) - ## information -_v1.3.2_ +_v1.4.0 unstable_ -changelog: [click here](./documentation/CHANGELOG.md) +changelog: [click here](./TEMPDOCS.md) © 2022 - DJdj Development | [website](https://www.dj-dj.be) | [discord](https://discord.com/invite/26vT9wt3n3) \ No newline at end of file diff --git a/TEMPDOCS.md b/TEMPDOCS.md new file mode 100644 index 0000000..57eaa2e --- /dev/null +++ b/TEMPDOCS.md @@ -0,0 +1,2 @@ +# documentation +The documentation is not available here, go to the [wiki](https://github.com/DJj123dj/open-ticket/wiki) instead. (the wiki isn't fully done yet!) \ No newline at end of file diff --git a/commands/database.js b/commands/database.js deleted file mode 100644 index 271f232..0000000 --- a/commands/database.js +++ /dev/null @@ -1,21 +0,0 @@ -const discord = require('discord.js') -const bot = require('../index') -const client = bot.client -const config = require("../config.json") - - -module.exports = () => { - client.on("messageCreate",(msg) => { - var args = msg.content.split(" ") - if (args[0] == config.prefix+"resetdatabase" || args[0] == config.prefix+"resetdb"){ - const databaseEmbed = new discord.MessageEmbed() - .setColor(config.main_color) - .setTitle("How to reset the database:") - .setDescription("1: Go to the folder `./storage` in your bot's directory.\n\n2: If posible remove the folders\n`./tickets`, `./transcripts` & `./userTickets`\nfrom the folder `./storage`\n\n3: Restart the bot") - .setFooter({text:"if it doesn't work, create an issue on github or DM me on discord!"}) - - msg.channel.send({embeds:[databaseEmbed]}) - if (config.logs){console.log("[command] "+config.prefix+"resetdatabase (user:"+msg.author.username+")")} - } - }) -} \ No newline at end of file diff --git a/commands/ticketExtra.js b/commands/extracmds.js similarity index 81% rename from commands/ticketExtra.js rename to commands/extracmds.js index cfa84c7..11c59c1 100644 --- a/commands/ticketExtra.js +++ b/commands/extracmds.js @@ -3,28 +3,11 @@ const bot = require('../index') const client = bot.client const config = require("../config.json") - +//============================= +// NOT READY YET +//============================= module.exports = () => { - client.on("messageCreate",msg => { - var args = msg.content.split(" ") - if (args[0] == config.prefix+"ticket" && (args[1] == undefined ||args[1] == "" ||args[1] == null)){ - var TicketHelpMsg = new discord.MessageEmbed() - .setColor(config.main_color) - .setTitle("Ticket commands") - - const prefix = config.prefix - TicketHelpMsg.setDescription("**Go to <#"+config.system.ticket_channel+"> to create a ticket!**\n\n`"+prefix+"ticket msg` ➜ _Admin command._\n`"+prefix+"ticket rename` ➜ _Rename a ticket (no spaces)._\n`"+prefix+"ticket close` ➜ _Close a ticket._\n`"+prefix+"ticket add ` ➜ _Add a user to the ticket._\n`"+prefix+"ticket remove ` ➜ _Remove a user from the ticket._\n`"+prefix+"resetdatabase` ➜ _Steps to reset the database._\n`"+prefix+"resetdb` ➜ _Also database stuff._") - - if (config.credits){ - TicketHelpMsg.setFooter({text:"Open-Ticket by DJdj Development | view on github for source code"}) - } - - msg.channel.send({embeds:[TicketHelpMsg]}) - if (config.logs){console.log("[command] "+config.prefix+"ticket help (user:"+msg.author.username+")")} - } - }) - client.on("messageCreate",msg => { var args = msg.content.split(" ") if (args[0] == config.prefix+"ticket" && args[1] == "close"){ diff --git a/commands/help.js b/commands/help.js new file mode 100644 index 0000000..9608613 --- /dev/null +++ b/commands/help.js @@ -0,0 +1,34 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = require("../config.json") + +module.exports = () => { + const helpEmbed = new discord.MessageEmbed() + .setColor(config.main_color) + .setTitle("Help Menu:") + + const prefix = config.prefix + helpEmbed.setDescription("**Go to <#"+config.system.ticket_channel+"> to create a ticket!**\n\n`"+prefix+"msg ` ➜ _Spawn an embed with buttons. (admin only)_\n\n`"+prefix+"rename ` ➜ _Rename a ticket. (no spaces)_\n`"+prefix+"close` ➜ _Close a ticket._\n`"+prefix+"delete` ➜ _Delete a ticket._\n\n`"+prefix+"add ` ➜ _Add a user to the ticket._\n`"+prefix+"remove ` ➜ _Remove a user from the ticket._`") + + if (config.credits) helpEmbed.setFooter({text:"Open-Ticket by DJdj Development | view on github for source code"}) + + + client.on("messageCreate",msg => { + if (!msg.content.startsWith(config.prefix)) return + var args = msg.content.split(config.prefix) + + 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("msg")){ + + msg.channel.send({embeds:[helpEmbed]}) + if (config.logs){console.log("[command] "+config.prefix+"ticket help (user:"+msg.author.username+")")} + } + }) + + client.on("interactionCreate",(interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "help") return + + interaction.reply({embeds:[helpEmbed]}) + }) +} \ No newline at end of file diff --git a/commands/ticket.js b/commands/ticket.js index 2ae1b5a..fce17e7 100644 --- a/commands/ticket.js +++ b/commands/ticket.js @@ -3,240 +3,60 @@ const bot = require('../index') const client = bot.client const config = require("../config.json") +/** ============================= + ** NOT READY YET + ** =============================*/ module.exports = () => { + /**@type {String[]} */ + var msgIds = [] + config.messages.forEach((msg) => { + msgIds.push(msg.id) + }) + + client.on("messageCreate", msg => { - if (msg.content == config.prefix+"ticket msg"){ - if (msg.member.roles.cache.has(config.botperms_role) == false){ - msg.channel.send({content:config.messages.general.nopermissions}) + if (msg.content.startsWith(config.prefix+"msg")){ + if (config.main_adminroles.some((item)=>{return msg.member.roles.cache.has(item)}) == false){ + msg.channel.send({content:"You have no permission to run this command!"}) return } - var currentTicketButtons = 0 - var ticketButton = new discord.MessageActionRow() - var ticketButton2 = new discord.MessageActionRow() + const id = msg.content.split(config.prefix+"msg")[1].substring(1) ? msg.content.split(config.prefix+"msg")[1].substring(1) : false - const getColor = (color) => { - if (color.toLowerCase() == "red"){ - return "DANGER" - }else if (color.toLowerCase() == "green"){ - return "SUCCESS" - }else if (color.toLowerCase() == "blue" || color.toLowerCase() == "blurple"){ - return "PRIMARY" - }else if (color.toLowerCase() == "black" || color.toLowerCase() == "gray" || color.toLowerCase() == "grey"){ - return "SECONDARY" - }else if (color == "DANGER" || color == "SECONDARY" || color == "SUCCESS" || color == "PRIMARY"){ - return color - }else if (color.toLowerCase() == "none" || color.toLowerCase() == "false" || color.toLowerCase() == ""){ - return "SECONDARY" - }else return "SECONDARY" - } + if (!id) return msg.channel.send({content:"**There is no id!**\nUse: `"+config.prefix+"msg `"}) + if (!msgIds.includes(id)) return msg.channel.send({content:"**Invalid id!**\nChoose from this list:\n_"+msgIds.join("_\n_")+"_"}) - if (config.options.ticket1.enabled){ - if (currentTicketButtons < 4){ - var localTicketButton = ticketButton - }else{ - var localTicketButton = ticketButton2 - } - currentTicketButtons = currentTicketButtons + 1 - - if (config.options.ticket1.isURL == false){ - localTicketButton.addComponents( - new discord.MessageButton() - .setCustomId("newTicket1") - .setDisabled(false) - .setStyle(getColor(config.options.ticket1.color)) - .setEmoji(config.options.ticket1.icon) - ) - }else{ - localTicketButton.addComponents( - new discord.MessageButton() - .setDisabled(false) - .setStyle("LINK") - .setEmoji(config.options.ticket1.icon) - .setURL(config.options.ticket1.url) - ) - } - } - if (config.options.ticket2.enabled){ - if (currentTicketButtons < 4){ - var localTicketButton = ticketButton - }else{ - var localTicketButton = ticketButton2 - } - - currentTicketButtons = currentTicketButtons + 1 - - if (config.options.ticket2.isURL == false){ - localTicketButton.addComponents( - new discord.MessageButton() - .setCustomId("newTicket2") - .setDisabled(false) - .setStyle(getColor(config.options.ticket2.color)) - .setEmoji(config.options.ticket2.icon) - ) - }else{ - localTicketButton.addComponents( - new discord.MessageButton() - .setDisabled(false) - .setStyle("LINK") - .setEmoji(config.options.ticket2.icon) - .setURL(config.options.ticket2.url) - ) - } - } - - if (config.options.ticket3.enabled){ - if (currentTicketButtons < 4){ - var localTicketButton = ticketButton - }else{ - var localTicketButton = ticketButton2 - } - - currentTicketButtons = currentTicketButtons + 1 - - if (config.options.ticket3.isURL == false){ - localTicketButton.addComponents( - new discord.MessageButton() - .setCustomId("newTicket3") - .setDisabled(false) - .setStyle(getColor(config.options.ticket3.color)) - .setEmoji(config.options.ticket3.icon) - ) - }else{ - localTicketButton.addComponents( - new discord.MessageButton() - .setDisabled(false) - .setStyle("LINK") - .setEmoji(config.options.ticket3.icon) - .setURL(config.options.ticket3.url) - ) - } - } - - if (config.options.ticket4.enabled){ - if (currentTicketButtons < 4){ - var localTicketButton = ticketButton - }else{ - var localTicketButton = ticketButton2 - } - - currentTicketButtons = currentTicketButtons + 1 - - if (config.options.ticket4.isURL == false){ - localTicketButton.addComponents( - new discord.MessageButton() - .setCustomId("newTicket4") - .setDisabled(false) - .setStyle(getColor(config.options.ticket4.color)) - .setEmoji(config.options.ticket4.icon) - ) - }else{ - localTicketButton.addComponents( - new discord.MessageButton() - .setDisabled(false) - .setStyle("LINK") - .setEmoji(config.options.ticket4.icon) - .setURL(config.options.ticket4.url) - ) - } - } - - if (config.options.ticket5.enabled){ - if (currentTicketButtons < 4){ - var localTicketButton = ticketButton - }else{ - var localTicketButton = ticketButton2 - } - - currentTicketButtons = currentTicketButtons + 1 - - if (config.options.ticket5.isURL == false){ - localTicketButton.addComponents( - new discord.MessageButton() - .setCustomId("newTicket5") - .setDisabled(false) - .setStyle(getColor(config.options.ticket5.color)) - .setEmoji(config.options.ticket5.icon) - ) - }else{ - localTicketButton.addComponents( - new discord.MessageButton() - .setDisabled(false) - .setStyle("LINK") - .setEmoji(config.options.ticket5.icon) - .setURL(config.options.ticket5.url) - ) - } - } - - if (config.options.ticket6.enabled){ - if (currentTicketButtons < 4){ - var localTicketButton = ticketButton - }else{ - var localTicketButton = ticketButton2 - } - - currentTicketButtons = currentTicketButtons + 1 - - if (config.options.ticket6.isURL == false){ - localTicketButton.addComponents( - new discord.MessageButton() - .setCustomId("newTicket6") - .setDisabled(false) - .setStyle(getColor(config.options.ticket6.color)) - .setEmoji(config.options.ticket6.icon) - ) - }else{ - localTicketButton.addComponents( - new discord.MessageButton() - .setDisabled(false) - .setStyle("LINK") - .setEmoji(config.options.ticket6.icon) - .setURL(config.options.ticket6.url) - ) - } - } - - var categorylist = "" - if (config.options.ticket1.enabled){categorylist = categorylist+"\n"+config.options.ticket1.icon+": **"+config.options.ticket1.name+"**\n"+config.options.ticket1.description} - - if (config.options.ticket2.enabled){categorylist = categorylist+"\n\n"+config.options.ticket2.icon+": **"+config.options.ticket2.name+"**\n"+config.options.ticket2.description} - - if (config.options.ticket3.enabled){categorylist = categorylist+"\n\n"+config.options.ticket3.icon+": **"+config.options.ticket3.name+"**\n"+config.options.ticket3.description} - - if (config.options.ticket4.enabled){categorylist = categorylist+"\n\n"+config.options.ticket4.icon+": **"+config.options.ticket4.name+"**\n"+config.options.ticket4.description} - - if (config.options.ticket5.enabled){categorylist = categorylist+"\n\n"+config.options.ticket5.icon+": **"+config.options.ticket5.name+"**\n"+config.options.ticket5.description} - - if (config.options.ticket6.enabled){categorylist = categorylist+"\n\n"+config.options.ticket6.icon+": **"+config.options.ticket6.name+"**\n"+config.options.ticket6.description} - - - - var ticketEmbed = new discord.MessageEmbed() - if (config.layout.ticketMsg.customColorEnabled){ - ticketEmbed.setColor(config.layout.ticketMsg.customColor) - }else{ticketEmbed.setColor(config.main_color)} - - if (config.layout.ticketMsg.footerEnabled){ - ticketEmbed.setFooter({text:config.layout.ticketMsg.footer}) - } - if (config.layout.ticketMsg.thumbnailEnabled){ - ticketEmbed.setThumbnail(config.layout.ticketMsg.thumbnailURL) - } - - ticketEmbed.setDescription("**Create a ticket:**\nClick one of the buttons below to create a ticket.\n\n__choose a category:__"+categorylist+"\n\n**watch out:**_You can only create "+config.system.max_allowed_tickets+" ticket(s) at a time!_") + const {embed,componentRows} = require("../core/ticketMessageEmbed").createEmbed(id) - - if (currentTicketButtons <= 4){ - var embedComponents = [ticketButton] - }else{var embedComponents = [ticketButton,ticketButton2]} - - msg.channel.send({embeds:[ticketEmbed],components:embedComponents}) + msg.channel.send({embeds:[embed],components:componentRows}) if (config.logs){console.log("[command] "+config.prefix+"ticket msg (user:"+msg.author.username+")")} if (config.logs){console.log("[system] created ticket message")} } }) + + client.on("interactionCreate", (interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "message") return + + if (config.main_adminroles.some((item)=>{return interaction.guild.members.cache.find((m) => m.id == interaction.member.id).roles.cache.has(item)}) == false){ + interaction.reply({content:"You have no permission to run this command!"}) + return + } + + const id = interaction.options.getString("id") + + if (!msgIds.includes(id)) return interaction.reply({content:"**Invalid id!**\nChoose from this list:\n_"+msgIds.join("_\n_")+"_"}) + + const {embed,componentRows} = require("../core/ticketMessageEmbed").createEmbed(id) + + interaction.reply({content:"The embed is in the message below!"}) + interaction.channel.send({embeds:[embed],components:componentRows}) + + if (config.logs){console.log("[command] "+config.prefix+"ticket msg (user:"+interaction.member.user.username+")")} + if (config.logs){console.log("[system] created ticket message")} + + }) } \ No newline at end of file diff --git a/commands/ticketSystem.js b/commands/ticketSystem.js deleted file mode 100644 index 6cf7ee4..0000000 --- a/commands/ticketSystem.js +++ /dev/null @@ -1,309 +0,0 @@ -const discord = require('discord.js') -const bot = require('../index') -const client = bot.client -const config = require("../config.json") - -const ticketStorage = bot.TicketStorage -const userTicketStorage = bot.userTicketStorage -const transcriptStorage = bot.transcriptStorage - -module.exports = () => { - - var closeButton = new discord.MessageActionRow() - .addComponents( - new discord.MessageButton() - .setCustomId("closeTicket") - .setDisabled(false) - .setStyle("SECONDARY") - .setLabel("Close Ticket") - .setEmoji("❌") - ) - - //ticket button click / create ticket - client.on("interactionCreate",interaction => { - - if (interaction.isButton() == false){ - return - } - - if (interaction.customId == "newTicket1"||interaction.customId == "newTicket2"||interaction.customId == "newTicket3"||interaction.customId == "newTicket4"||interaction.customId == "newTicket5"||interaction.customId == "newTicket6"){ - - interaction.deferUpdate() - - if (ticketStorage.getItem(interaction.member.id) == null ||ticketStorage.getItem(interaction.member.id) == "false"|| Number(ticketStorage.getItem(interaction.member.id)) < config.system.max_allowed_tickets){ - - try{ - if (config.system.enable_DM_Messages){ - interaction.member.send(config.messages.dm.newTicket) - } - } - catch{console.log("can't send DM to member || member doesn't allow dm's")} - - - //update storage - ticketStorage.setItem(interaction.member.id,Number(ticketStorage.getItem(interaction.member.id))+1) - var ticketNumber = interaction.member.user.username - - //set ticketName - if (interaction.customId == "newTicket1"){ - var ticketName = config.options.ticket1.channel_prefix+ticketNumber - var logsname = config.options.ticket1.name - }else if (interaction.customId == "newTicket2"){ - var ticketName = config.options.ticket2.channel_prefix+ticketNumber - var logsname = config.options.ticket2.name - }else if (interaction.customId == "newTicket3"){ - var ticketName = config.options.ticket3.channel_prefix+ticketNumber - var logsname = config.options.ticket3.name - }else if (interaction.customId == "newTicket4"){ - var ticketName = config.options.ticket4.channel_prefix+ticketNumber - var logsname = config.options.ticket4.name - }else if (interaction.customId == "newTicket5"){ - var ticketName = config.options.ticket5.channel_prefix+ticketNumber - var logsname = config.options.ticket5.name - }else if (interaction.customId == "newTicket6"){ - var ticketName = config.options.ticket6.channel_prefix+ticketNumber - var logsname = config.options.ticket6.name - } - - //set category - if (config.system.enable_category){ - var Category = config.system.ticket_category - }else{var Category = null} - - var permissionsArray = [] - - //set everyone allowed - if (config.system['has@everyoneaccess']){ - var everyoneAllowPerms = ["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] - var everyoneDenyPerms = [] - }else{ - var everyoneAllowPerms = [] - var everyoneDenyPerms = ["VIEW_CHANNEL"] - } - permissionsArray.push({ - id:interaction.member.id, - type:"member", - allow:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] - }) - permissionsArray.push({ - id:config.botperms_role, - type:"role", - allow:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] - }) - permissionsArray.push({ - id:interaction.guild.id, - type:"role", - allow:everyoneAllowPerms, - deny:everyoneDenyPerms - }) - if (config.system.member_role == "" || config.system.member_role == " " || config.system.member_role == "false" || config.system.member_role == "null" || config.system.member_role == "0"){ - permissionsArray.push({ - id:config.system.member_role, - type:"role", - deny:["VIEW_CHANNEL"] - }) - } - - //create the channel - interaction.guild.channels.create(ticketName,{ - type:"GUILD_TEXT", - parent:Category, - permissionOverwrites:permissionsArray - }).then(tChannel => { - userTicketStorage.setItem(tChannel.id,interaction.member.id) - - - var ticketEmbed = new discord.MessageEmbed() - if (config.layout.ticketEmbed.customColorEnabled){ - ticketEmbed.setColor(config.layout.ticketEmbed.customColor) - }else{ticketEmbed.setColor(config.main_color)} - - if (config.layout.ticketEmbed.footerEnabled){ - ticketEmbed.setFooter({text:"config.layout.ticketEmbed.footer"}) - } - if (config.layout.ticketEmbed.thumbnailEnabled){ - ticketEmbed.setThumbnail(config.layout.ticketEmbed.thumbnailURL) - } - ticketEmbed.setTitle("You created a ticket!") - ticketEmbed.setDescription(config.messages.ticket.newTicketEmbed) - - tChannel.send({content:"<@"+interaction.member.id+"> <@&"+config.botperms_role+">",embeds:[ticketEmbed],components:[closeButton]}).then(firstmsg => { - firstmsg.pin() - }) - if (config.logs){console.log("[system] created a new ticket (name:"+logsname+",user:"+interaction.user.username+")")} - }) - - - - }else{ - try { - if (config.system.enable_DM_Messages){ - interaction.member.send(config.messages.dm.alreadyCreated) - } - } - catch{console.log("can't send DM to member || member doesn't allow dm's")} - - - } - } - - - - }) - - //closeBAR - var closeBar = new discord.MessageActionRow() - .addComponents( - new discord.MessageButton() - .setCustomId("closeTicketTrue") - .setDisabled(false) - .setStyle("SECONDARY") - .setEmoji("✅") - ) - .addComponents( - new discord.MessageButton() - .setCustomId("closeTicketFalse") - .setDisabled(false) - .setStyle("SECONDARY") - .setEmoji("❌") - ) - - - - client.on("interactionCreate",interaction => { - if (interaction.isButton() == false){ - return - } - if (interaction.customId == "closeTicket"){ - interaction.deferUpdate() - interaction.message.edit({components:[closeBar]}) - } - }) - - client.on("interactionCreate",interaction => { - if (interaction.isButton() == false){ - return - } - if (interaction.customId == "closeTicketFalse"){ - interaction.deferUpdate() - - interaction.message.edit({components:[closeButton]}) - } - }) - - - - client.on("interactionCreate",interaction => { - if (interaction.isButton() == false){ - return - } - if (interaction.customId == "closeTicketTrue"){ - interaction.deferUpdate() - interaction.channel.messages.fetch().then(messages => { - var transcriptArray = [] - messages.forEach(msg => { - if (msg.author.id != client.user.id){ - transcriptArray.push("["+msg.author.username+"]: "+msg.content) - } - }) - var getuserID = userTicketStorage.getItem(interaction.channel.id) - var getusernameStep1 = client.users.cache.find(u => u.id === getuserID) - if (getusernameStep1 == null || getusernameStep1 == undefined || getusernameStep1 == false || getusernameStep1 == ""){ - var getuserNAME = ":usernotfound:" - }else { - var getuserNAME = getusernameStep1.username - } - - - if (config.system.enable_transcript){ - var transcript = transcriptArray.reverse().join("\n") - transcriptStorage.setItem(interaction.channel.id,transcript) - - //transcript color & thumbnail - if (config.layout.transcripts.customColorEnabled){ - var transcriptColor = config.layout.transcripts.customColor - }else{var transcriptColor = config.main_color} - - if (config.layout.transcripts.thumbnailEnabled){ - var transcriptThumbnail = config.layout.transcripts.thumbnailURL - }else{var transcriptThumbnail = ""} - - var splittedTranscript = [transcript.slice(0,2000)] - if (transcript.length > 4000){ - splittedTranscript.push(transcript.slice(2000,4000)) - splittedTranscript.push(transcript.slice(4000)) - }else if (transcript.length > 2000){ - splittedTranscript.push(transcript.slice(2000)) - } - var transcriptEmbed = new discord.MessageEmbed() - .setColor(transcriptColor) - .setAuthor({name:interaction.channel.name + " - ticket created by "+getuserNAME}) - .setTitle("There is a new transcript!") - .setDescription(splittedTranscript[0]) - .setFooter({text:"ticket closed by "+interaction.member.user.username}) - .setThumbnail(transcriptThumbnail) - - if (transcript.length > 4000){ - var transcriptEmbed2 = new discord.MessageEmbed() - .setColor(transcriptColor) - .setAuthor({name:interaction.channel.name + " - ticket created by "+getuserNAME}) - .setTitle("transcript #2") - .setDescription(splittedTranscript[1]) - .setFooter("ticket closed by "+interaction.member.user.username) - .setThumbnail(transcriptThumbnail) - - var transcriptEmbed3 = new discord.MessageEmbed() - .setColor(transcriptColor) - .setAuthor({name:interaction.channel.name + " - ticket created by "+getuserNAME}) - .setTitle("transcript #3") - .setDescription(splittedTranscript[1]) - .setFooter({text:"ticket closed by "+interaction.member.user.username}) - .setThumbnail(transcriptThumbnail) - - client.channels.cache.find(ch => ch.id == config.system.transcript_channel).send({embeds:[transcriptEmbed,transcriptEmbed2,transcriptEmbed3]}) - - }else if (transcript.length > 2000){ - var transcriptEmbed2 = new discord.MessageEmbed() - .setColor(transcriptColor) - .setAuthor({name:interaction.channel.name + " - ticket created by "+getuserNAME}) - .setTitle("transcript #2") - .setDescription(splittedTranscript[1]) - .setFooter({text:"ticket closed by "+interaction.member.user.username}) - .setThumbnail(transcriptThumbnail) - - client.channels.cache.find(ch => ch.id == config.system.transcript_channel).send({embeds:[transcriptEmbed,transcriptEmbed2]}) - }else{ - client.channels.cache.find(ch => ch.id == config.system.transcript_channel).send({embeds:[transcriptEmbed]}) - } - } - - - - interaction.channel.delete() - if (config.logs){console.log("[system] closed a ticket (name:"+interaction.channel.name+",user:"+interaction.user.username+")")} - - - - ticketStorage.setItem(getuserID,Number(ticketStorage.getItem(getuserID)) - 1) - - try { - if (config.system.enable_DM_Messages){ - interaction.member.send(config.messages.dm.closeTicket) - } - } - catch{console.log("can't send DM to member || member doesn't allow dm's")} - }) - - - - - } - }) - - - - process.on('unhandledRejection',error => { - console.log("ERROR: "+error) - }) - -} \ No newline at end of file diff --git a/config.json b/config.json index cea96cc..baad7e3 100644 --- a/config.json +++ b/config.json @@ -1,132 +1,94 @@ { "bot_name":"Wumpus", "main_color":"#fffff", - "auth_token":"the bot's auth token", - "botperms_role":"the id from an admin role (they can do admin things with the bot)", - "prefix":"!", + "server_id":"935983011281903676", + "auth_token":"OTU4MjkwNjExMzMxNTM4OTc1.YkLLrQ.J9yfO7LgXplNahXv7xQNNvKG6fY", + "main_adminroles":["936153662668034058"], + "prefix":"!ticket ", "logs":true, + "languagefile":"english.json (currently not working)", "credits":true, "status":{ "enabled":true, - "type":"PLAYING | LISTENING | WATCHING | CUSTOM", + "type":"PLAYING", "text":"!ticket" }, "system":{ - "ticket_channel":"id of the channel with the !ticket msg", - "max_allowed_tickets":5, + "ticket_channel":"968899402020306994", + "max_allowed_tickets":3, "enable_DM_Messages":true, - "enable_category":false, - "ticket_category":"the id of a category for the tickets", - "has@everyoneaccess":false, - "member_role":"this role doesn't have access to tickets", + "member_role":"936153574650544180", - "enable_transcript":false, - "transcript_channel":"the channel for transcripts" + "enable_transcript":true, + "enable_DM_transcript":true, + "transcript_channel":"968899606912061490" }, - "messages":{ - "general":{ - "nopermissions":"**You don't have permissions to run this command!**" - }, - "dm":{ - "alreadyCreated":"**You currently have as many open tickets as the maximum allowed!**", - "newTicket":"You created a new ticket in our server, the staff will come and help you soon!", - "closeTicket":"**Your ticket is closed!**" - }, - "ticket":{ - "newTicketEmbed":"You created a ticket!\n\n_Click on the close button below to close this ticket_" - } - }, - - "layout":{ - "transcripts":{ - "customColorEnabled":false, - "customColor":"#ffffff", - "thumbnailEnabled":false, - "thumbnailURL":"https://www.example.com/path/to/image.png" - }, - "ticketMsg":{ - "customColorEnabled":false, - "customColor":"#ffffff", - "footerEnabled":false, - "footer":"We are using open-tickets from DJj123dj", - "thumbnailEnabled":false, - "thumbnailURL":"https://www.example.com/path/to/image.png" - }, - "ticketEmbed":{ - "customColorEnabled":false, - "customColor":"#ffffff", - "footerEnabled":false, - "footer":"This is embed in the start of a ticket.", - "thumbnailEnabled":false, - "thumbnailURL":"https://www.example.com/path/to/image.png" - } - }, - - "options":{ - "ticket1":{ - "enabled":true, - "icon":"🎫", - "description":"Create a general ticket.", - "name":"Ticket", - "channel_prefix":"ticket-", - "color":"red | green | blue | gray | none", - "isURL":false, - "url":"https://www.example.com/a/random/path/to/a/website" - }, - "ticket2":{ - "enabled":false, - "icon":"💼", - "description":"Do you want to apply? Then click here!", - "name":"Apply", - "channel_prefix":"apply-", - "color":"red | green | blue | gray | none", - "isURL":false, - "url":"https://www.example.com/a/random/path/to/a/website" - }, - "ticket3":{ - "enabled":false, - "icon":"❓", - "description":"Click here if you have a question about the server.", + "options":[ + { + "id":"general", "name":"Question", - "channel_prefix":"question-", - "color":"red | green | blue | gray | none", - "isURL":false, - "url":"https://www.example.com/a/random/path/to/a/website" + "description":"Create this ticket if you have a question.", + "icon":"🎫", + "label":"question", + "type":"ticket", + + "color":"gray", + "adminroles":["936153350704091167"], + "channelprefix":"question-", + "category":"935983011281903678", + "message":"You created a ticket!", + "enableDMMessage":true + }, - "ticket4":{ - "enabled":false, - "icon":"🛒", - "description":"Buy something in our shop.", - "name":"Shop", - "channel_prefix":"shop-", - "color":"red | green | blue | gray | none", - "isURL":false, - "url":"https://www.example.com/a/random/path/to/a/website" - }, - "ticket5":{ - "enabled":false, - "icon":"😄", - "description":"hello", - "name":"AnotherTicket", - "channel_prefix":"prefix-", - "color":"red | green | blue | gray | none", - "isURL":false, - "url":"https://www.example.com/a/random/path/to/a/website" - }, - "ticket6":{ - "enabled":false, - "icon":"😄", - "description":"hello", - "name":"AnotherTicket", - "channel_prefix":"prefix-", - "color":"red | green | blue | gray | none", - "isURL":false, - "url":"https://www.example.com/a/random/path/to/a/website" + { + "id":"apply", + "name":"Apply", + "description":"Create this ticket if you want to apply.", + "icon":"💼", + "label":"apply", + "type":"ticket", + + "color":"green", + "adminroles":["936153350704091167"], + "channelprefix":"apply-", + "category":"935983011281903678", + "message":"You created a ticket!", + "enableDMMessage":true + } - } + ], + + "messages":[ + { + "id":"1", + "name":"Choose Your Ticket", + "description":"You can click one of the buttons below!\nYou can also customize all the embeds now!", + "enableFooter":true, + "footer":"Open Ticket v1.4.0 beta - sneakpeak", + "enableThumbnail":false, + "thumbnail":"https://www.example.com/catmemes/cat.png", + "color":"#0075e9", + "options":["general","apply"], + "enableTicketExplaination":true, + "enableMaxTicketsWarning":true + }, + { + "id":"5", + "name":"sodfjiqdosmifj", + "description":"Youoidjfddiddjfmqoisdjf so customize all the embeds now!", + "enableFooter":true, + "footer":"Open Tiiiieakpeak", + "enableThumbnail":false, + "thumbnail":"https://www.example.com/catmemes/cat.png", + "color":"#0075e9", + "options":["apply"], + "enableTicketExplaination":true, + "enableMaxTicketsWarning":true + } + ] } \ No newline at end of file diff --git a/checker.js b/core/checker.js similarity index 98% rename from checker.js rename to core/checker.js index 0bdec6d..4f0f219 100644 --- a/checker.js +++ b/core/checker.js @@ -1,6 +1,10 @@ +/** ============================= + ** NOT READY YET + ** =============================*/ + module.exports = async () => { const chalk = await (await import("chalk")).default - const config = require("./config.json") + const config = require("../config.json") var errorList = [] var isError = false var warnList = [] diff --git a/core/closebuttons.js b/core/closebuttons.js new file mode 100644 index 0000000..0cd6ea9 --- /dev/null +++ b/core/closebuttons.js @@ -0,0 +1,206 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = require("../config.json") + + +module.exports = () => { + //closebar + var closeBar = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("closeTicketTrue") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("✅") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("closeTicketFalse") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("❌") + ) + var deleteBar = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicketTrue") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("✅") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicketFalse") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("❌") + ) + var deleteBar1 = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicketTrue1") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("✅") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicketFalse1") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("❌") + ) + //closebutton + var closeRowNormal = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("closeTicket") + .setDisabled(false) + .setStyle("SECONDARY") + .setLabel("Close Ticket") + .setEmoji("🔒") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicket") + .setDisabled(false) + .setStyle("DANGER") + .setLabel("Delete Ticket") + .setEmoji("❌") + ) + var closeRowClosed = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicket1") + .setDisabled(false) + .setStyle("DANGER") + .setLabel("Delete Ticket") + .setEmoji("❌") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("sendTranscript") + .setDisabled(false) + .setStyle("SECONDARY") + .setLabel("Send Transcript File") + .setEmoji("📄") + ) + + + //NORMAL CLOSE + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "closeTicket") return + + interaction.deferUpdate() + interaction.message.edit({components:[closeBar]}) + + }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "closeTicketFalse") return + + interaction.deferUpdate() + interaction.message.edit({components:[closeRowNormal]}) + }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "closeTicketTrue") return + + interaction.deferUpdate() + interaction.message.edit({components:[closeRowClosed]}) + + /** + * @type {String} + */ + const name = interaction.channel.name + var prefix = "" + const tickets = config.options + tickets.forEach((ticket) => { + if (name.startsWith(ticket.channelprefix)){ + prefix = ticket.channelprefix + } + }) + + require("./ticketCloser").closeTicket(interaction,prefix,"close") + }) + + + //NORMAL DELETE + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "deleteTicket") return + + interaction.deferUpdate() + interaction.message.edit({components:[deleteBar]}) + + }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "deleteTicketFalse") return + + interaction.deferUpdate() + interaction.message.edit({components:[closeRowNormal]}) + }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "deleteTicketTrue") return + + interaction.deferUpdate() + + /** + * @type {String} + */ + const name = interaction.channel.name + var prefix = "" + const tickets = config.options + tickets.forEach((ticket) => { + if (name.startsWith(ticket.channelprefix)){ + prefix = ticket.channelprefix + } + }) + + require("./ticketCloser").closeTicket(interaction,prefix,"delete") + }) + + + + + //CLOSED DELETE + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "deleteTicket1") return + + interaction.deferUpdate() + interaction.message.edit({components:[deleteBar1]}) + + }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "deleteTicketFalse1") return + + interaction.deferUpdate() + interaction.message.edit({components:[closeRowClosed]}) + }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "deleteTicketTrue1") return + + interaction.deferUpdate() + + /** + * @type {String} + */ + const name = interaction.channel.name + var prefix = "" + const tickets = config.options + tickets.forEach((ticket) => { + if (name.startsWith(ticket.channelprefix)){ + prefix = ticket.channelprefix + } + }) + + require("./ticketCloser").closeTicket(interaction,prefix,"deletenotranscript") + }) +} \ No newline at end of file diff --git a/storage/storage.js b/core/dynamicdatabase/storage.js similarity index 83% rename from storage/storage.js rename to core/dynamicdatabase/storage.js index 67d979b..1af2cff 100644 --- a/storage/storage.js +++ b/core/dynamicdatabase/storage.js @@ -1,3 +1,7 @@ +/** ============================= + ** NOT READY YET + ** must be dynamic + ** =============================*/ const LocalStorage = require("node-localstorage") const ticketStorage = new LocalStorage.LocalStorage("./storage/tickets") diff --git a/core/getoptions.js b/core/getoptions.js new file mode 100644 index 0000000..60ff236 --- /dev/null +++ b/core/getoptions.js @@ -0,0 +1,113 @@ +const config = require("../config.json") + +/** + * @param {Number} index + * @param {"id"|"name"|"description"|"icon"|"label"|"type"|"color"|"adminroles"|"channelprefix"|"category"|"message"|"enableDMMessage"} value + */ +exports.getTicketValue = (index,value) => { + if (value == "id"){ + return "newT"+config.options[index][value] + } + return config.options[index][value] +} + +/** + * @param {Number} index + * @param {"id"|"name"|"description"|"icon"|"label"|"type"|"url"} value + */ + exports.getWebsiteValue = (index,value) => { + return config.options[index][value] +} + +/** + * @param {Number} index + * @param {"id"|"name"|"description"|"icon"|"label"|"type"|"color"|"roles"|"mode"|"enableDMMessage"} value + */ +exports.getRoleValue = (index,value) => { + return config.options[index][value] +} + +/** + * @param {"id"|"name"|"description"|"icon"|"label"|"type"|"color"|"adminroles"|"channelprefix"|"category"|"message"|"enableDMMessage"} value + */ + exports.getTicketValues = (value) => { + var result = [] + config.options.forEach((o) => { + if (value == "id"){ + result.push("newT"+o[value]) + }else{ + result.push(o[value]) + } + }) + return result +} + +/** + * @param {"id"|"name"|"description"|"icon"|"label"|"type"|"url"} value + */ + exports.getWebsiteValues = (value) => { + var result = [] + config.options.forEach((o) => { + result.push(o[value]) + }) + return result +} + +/** + * @param {"id"|"name"|"description"|"icon"|"label"|"type"|"color"|"roles"|"mode"|"enableDMMessage"} value + */ +exports.getRoleValues = (value) => { + var result = [] + config.options.forEach((o) => { + result.push(o[value]) + }) + return result +} + +class TicketOptions { + constructor() { + this.id = "" + this.name = "" + this.description = "" + this.icon = "" + this.label = "" + this.type = "" + this.color = "" + this.adminroles = ["",""] + this.channelprefix = "" + this.category = "" + this.message = "" + this.enableDMMessage = true + } +} +/** + * + * @param {String} id + * @returns {TicketOptions|false} + */ +exports.getOptionsById = (id) => { + var result = false + config.options.forEach((option) => { + if ("newT"+option.id == id){ + result = option + } + }) + return result +} + +exports.exportedOptionClass = class { + constructor() { + this.id = "" + this.name = "" + this.description = "" + this.icon = "" + this.label = "" + this.type = "" + this.color = "" + this.adminroles = ["",""] + this.channelprefix = "" + this.category = "" + this.message = "" + this.enableDMMessage = true + } +} \ No newline at end of file diff --git a/core/slashSystem/slashDisable.js b/core/slashSystem/slashDisable.js new file mode 100644 index 0000000..0eef12a --- /dev/null +++ b/core/slashSystem/slashDisable.js @@ -0,0 +1,23 @@ +const discord = require("discord.js") +const client = require("../../index").client +const config = require("../../config.json") +const getoptions = require("../getoptions") + +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.isCommand()) 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!")) + } + }) +} \ No newline at end of file diff --git a/core/slashSystem/slashEnable.js b/core/slashSystem/slashEnable.js new file mode 100644 index 0000000..fd0e448 --- /dev/null +++ b/core/slashSystem/slashEnable.js @@ -0,0 +1,162 @@ +const discord = require("discord.js") +const client = require("../../index").client +const config = require("../../config.json") +const getoptions = require("../getoptions") + +module.exports = async () => { + const chalk = (await import("chalk")).default + const sid = config.server_id + + const ids = getoptions.getTicketValues("id") + /**@type {[{name:String,value:String}]} */ + var choices = [] + ids.forEach((id) => { + const option = getoptions.getOptionsById(id) + console.log(option.id) + 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 + + setInterval(() => { + console.log("[status] there are "+chalk.blue(readystats+" out of 8")+" commands ready! (this can take some time)") + if (readystats >= 8){ + console.log(chalk.green("ready!")) + console.log(chalk.blue("you can now start the bot with 'npm start'!")) + process.exit(1) + } + },1000) + + //create a ticket (/new or /ticket) + client.application.commands.create({ + name:"ticket", + description:"Create a ticket!", + defaultPermission:true, + type:"CHAT_INPUT", + options:[ + { + type:"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:"CHAT_INPUT", + options:[ + { + type:"STRING", + name:"type", + description:"The type of ticket.", + required:true, + choices:choices + + } + ] + },sid).then(() => { + readystats++ + }) + + //help + client.application.commands.create({ + name:"help", + description:"The help menu", + defaultPermission:true, + type:"CHAT_INPUT" + },sid).then(() => { + readystats++ + + }) + + //close + client.application.commands.create({ + name:"close", + description:"Close the current ticket.", + defaultPermission:true, + type:"CHAT_INPUT" + },sid).then(() => { + readystats++ + }) + + //delete + client.application.commands.create({ + name:"delete", + description:"Delete the current ticket.", + defaultPermission:true, + type:"CHAT_INPUT" + },sid).then(() => { + readystats++ + }) + + //msg + client.application.commands.create({ + name:"message", + description:"Create a ticket message.", + defaultPermission:true, + type:"CHAT_INPUT", + options:[ + { + type:"STRING", + name:"id", + description:"The message id.", + required:true, + choices:msgchoices + + } + ] + },sid).then(() => { + readystats++ + }) + + //add + client.application.commands.create({ + name:"add", + description:"Add people to this ticket.", + defaultPermission:true, + type:"CHAT_INPUT", + options:[ + { + type:"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, + type:"CHAT_INPUT", + options:[ + { + type:"USER", + name:"user", + description:"The user to remove.", + required:true + } + ] + },sid).then(() => { + readystats++ + }) + +} \ No newline at end of file diff --git a/core/ticketCloser.js b/core/ticketCloser.js new file mode 100644 index 0000000..ffbd234 --- /dev/null +++ b/core/ticketCloser.js @@ -0,0 +1,153 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = require("../config.json") + +const ticketStorage = bot.TicketStorage +const userTicketStorage = bot.userTicketStorage +const transcriptStorage = bot.transcriptStorage + +/** + * + * @param {discord.Interaction} interaction + * @param {String} prefix + * @param {"delete"|"close"|"deletenotranscript"} mode + */ +exports.closeTicket = async (interaction,prefix,mode) => { + const channelmessages = await interaction.channel.messages.fetch() + + channelmessages.sweep((msgSweep) => { + return msgSweep.author.id == client.user.id + }) + + /** + * @type {String} ticketuserarray + */ + const ticketuserarray = interaction.channel.name + const ticketusername = ticketuserarray.split(prefix)[1] + + var getuserID = userTicketStorage.getItem(interaction.channel.id) + var getusernameStep1 = client.users.cache.find(u => u.id === getuserID) + + var enableTranscript = true + + if (mode == "delete"){ + interaction.channel.delete() + if (config.logs){console.log("[system] deleted a ticket (name:"+interaction.channel.name+",user:"+interaction.user.username+")")} + + ticketStorage.setItem(getuserID,Number(ticketStorage.getItem(getuserID)) - 1) + }else if (mode == "close"){ + var permissionArray = [] + permissionArray.push({ + id:getusernameStep1.id, + type:"member", + allow:["VIEW_CHANNEL"], + deny:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES"] + }) + + permissionArray.push({ + id:interaction.guild.id, + type:"role", + deny:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] + }) + + interaction.channel.permissionOverwrites.set(permissionArray) + + //message + var closeButtonRow = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicket1") + .setDisabled(false) + .setStyle("DANGER") + .setLabel("Delete Ticket") + .setEmoji("❌") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("sendTranscript") + .setDisabled(false) + .setStyle("SECONDARY") + .setLabel("Send Transcript File") + .setEmoji("📄") + ) + const embed = new discord.MessageEmbed() + .setColor(config.main_color) + .setTitle("Closed this ticket!") + .setDescription("Only admins can now talk in this ticket!\n\n*Click on the button below to delete this ticket*") + interaction.channel.send({embeds:[embed],components:[closeButtonRow]}) + }else if (mode == "deletenotranscript"){ + enableTranscript = false + interaction.channel.delete() + if (config.logs){console.log("[system] deleted a ticket (name:"+interaction.channel.name+",user:"+interaction.user.username+")")} + + ticketStorage.setItem(getuserID,Number(ticketStorage.getItem(getuserID)) - 1) + } + + if (enableTranscript == true && mode != "deletenotranscript"){ + + if (config.system.enable_transcript || config.system.enable_DM_transcript){ + var fileattachment = await require("./transcript").createTranscript(channelmessages) + + if (fileattachment == false){console.log("[transcript] internal error: no transcript is created!");return} + } + + if (getusernameStep1 == null || getusernameStep1 == undefined || getusernameStep1 == false || getusernameStep1 == ""){ + var getuserNAME = ":usernotfound:" + }else { + var getuserNAME = getusernameStep1.username + } + + if (config.system.enable_transcript){ + const transcriptEmbed = new discord.MessageEmbed() + .setColor(config.main_color) + .setTitle("A new transcript is here!") + .setAuthor({name:interaction.user.username,iconURL:interaction.user.displayAvatarURL({format:"png"})}) + .setDescription("You can find the transcript in the text file above!") + .setFooter({text:"ticket: "+ticketuserarray}) + + interaction.guild.channels.cache.find(c => c.id == config.system.transcript_channel).send({ + embeds:[transcriptEmbed], + files:[fileattachment] + }) + } + + if (config.system.enable_DM_transcript){ + const transcriptEmbed = new discord.MessageEmbed() + .setColor(config.main_color) + .setTitle("Here is a transcript of your ticket!") + .setDescription("You can find the transcript in the text file above!") + .setFooter({text:"ticket: "+ticketuserarray}) + + getusernameStep1.send({ + embeds:[transcriptEmbed], + files:[fileattachment] + }) + } + } +} + +exports.runThis = () => { + client.on("interactionCreate",async (interaction) => { + if (!interaction.isButton()) return + if (interaction.customId != "sendTranscript") return + + interaction.deferUpdate() + + const channelmessages = await interaction.channel.messages.fetch() + + channelmessages.sweep((msgSweep) => { + return msgSweep.author.id == client.user.id + }) + + var fileattachment = await require("./transcript").createTranscript(channelmessages) + + if (fileattachment == false){ + console.log("[transcript] internal error: no transcript is created!") + interaction.channel.send({content:"**Something went wrong while making the transcript!**\nPlease try again another time!"}) + return + } + + interaction.channel.send({content:"**Here is the transcript:**",files:[fileattachment]}) + }) +} \ No newline at end of file diff --git a/core/ticketMessageEmbed.js b/core/ticketMessageEmbed.js new file mode 100644 index 0000000..2d85bee --- /dev/null +++ b/core/ticketMessageEmbed.js @@ -0,0 +1,133 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = require("../config.json") +const getOptions = require("./getoptions") +const getbyId = getOptions.getOptionsById + +/** + * + * @param {String} id the id from an open-ticket embed + * @returns {{embed:discord.MessageEmbed,componentRows:discord.MessageActionRow[]}} discord embed + */ + + + +exports.createEmbed = (id) => { + //general importing + const data = require("./utils/getTicketMessages").getTicketMessage(id) + /**@type {getOptions.exportedOptionClass[]}*/ + var buttons = [] + + data.options.forEach((option) => { + var optionIdStats = getbyId("newT"+option) + + if (!optionIdStats){ + var optionIdStats = { + id:"NOTICKET", + name:"ERROR", + description:"There is a ticket in your config.json/messages that doesn't exist.", + icon:"", + label:"ERROR", + type:"ticket", + color:"red", + adminroles:["0"], + channelprefix:"error-", + category:"", + message:"ERROR!", + enableDMMessage:false + + } + return + } + buttons.push(optionIdStats) + }) + + //create the embed + const embed = new discord.MessageEmbed() + .setColor(data.color) + .setTitle(data.name) + if (data.enableFooter) embed.setFooter({text:data.footer}) + if (data.enableThumbnail) embed.setThumbnail(data.thumbnail) + + var description = data.description + if (data.enableTicketExplaination){ + description = description+"\n\n__choose a category:__\n" + + var ticketExplainations = [] + buttons.forEach((button) => { + var explaination = button.icon+" **"+button.name+":**\n"+button.description + ticketExplainations.push(explaination) + }) + + description = description+ticketExplainations.join("\n\n") + } + + if (data.enableMaxTicketsWarning){ + description = description+"\n\n**Warning:** _You can only create "+config.system.max_allowed_tickets+" ticket(s) at a time!_" + } + + embed.setDescription(description) + + //create the components + var AmountOfRows = Math.ceil(buttons.length / 4) + var currentRow = 0 + + /** + * @type {discord.MessageActionRow[]} + */ + var componentRows = [] + + for (let i = AmountOfRows; i > 0; i--){ + + componentRows.push(new discord.MessageActionRow()) + } + + const getColor = (color) => { + if (color.toLowerCase() == "red"){ + return "DANGER" + }else if (color.toLowerCase() == "green"){ + return "SUCCESS" + }else if (color.toLowerCase() == "blue" || color.toLowerCase() == "blurple"){ + return "PRIMARY" + }else if (color.toLowerCase() == "black" || color.toLowerCase() == "gray" || color.toLowerCase() == "grey"){ + return "SECONDARY" + }else if (color == "DANGER" || color == "SECONDARY" || color == "SUCCESS" || color == "PRIMARY"){ + return color + }else if (color.toLowerCase() == "none" || color.toLowerCase() == "false" || color.toLowerCase() == ""){ + return "SECONDARY" + }else return "SECONDARY" + } + + buttons.forEach((button,index) => { + if (button.label){var label = button.label}else{var label = null} + if (button.icon){var icon = button.icon}else{var icon = null} + + var color = getColor(button.color) + + componentRows[currentRow].addComponents([ + new discord.MessageButton() + .setCustomId("newT"+button.id) + .setLabel(label) + .setEmoji(icon) + .setStyle(color) + .setDisabled(false) + ]) + + if (index == 3 || index == 7 || index == 11 || index == 15 || index == 19 || index == 23 || index == 27 || index == 31 || index == 35 || index == 39){ + currentRow = currentRow + 1 + } + }) + + + + //return everything + /** + * @type {{embed:discord.MessageEmbed,componentRows:discord.MessageActionRow[]}} + */ + const returnedData = {embed:embed,componentRows:componentRows} + return returnedData +} + + +this.createEmbed() \ No newline at end of file diff --git a/core/ticketOpener.js b/core/ticketOpener.js new file mode 100644 index 0000000..81b3dfb --- /dev/null +++ b/core/ticketOpener.js @@ -0,0 +1,171 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = require("../config.json") + +const getconfigoptions = require("./getoptions") + +const ticketStorage = bot.TicketStorage +const userTicketStorage = bot.userTicketStorage +const transcriptStorage = bot.transcriptStorage + +module.exports = () => { + var closeButton = new discord.MessageActionRow() + .addComponents( + new discord.MessageButton() + .setCustomId("closeTicket") + .setDisabled(false) + .setStyle("SECONDARY") + .setLabel("Close Ticket") + .setEmoji("🔒") + ) + .addComponents( + new discord.MessageButton() + .setCustomId("deleteTicket") + .setDisabled(false) + .setStyle("DANGER") + .setLabel("Delete Ticket") + .setEmoji("❌") + ) + + //ticket button click / create ticket + client.on("interactionCreate",(interaction) => { + if (interaction.isCommand() && (interaction.commandName == "new" || interaction.commandName == "ticket")){ + var customId = "newT"+interaction.options.getString("type") + }else if (interaction.isButton()){ + var customId = interaction.customId + }else return + + if (getconfigoptions.getTicketValues("id").includes(customId)){ + + if (interaction.isButton()){ + interaction.deferUpdate() + }else if (interaction.isCommand()){ + interaction.reply({content:"Your ticket is created!"}) + } + + //ticketoptions from config + const currentTicketOptions = getconfigoptions.getOptionsById(customId) + + if (ticketStorage.getItem(interaction.member.id) == null || ticketStorage.getItem(interaction.member.id) == "false"|| Number(ticketStorage.getItem(interaction.member.id)) < config.system.max_allowed_tickets){ + + try{ + if (config.system.enable_DM_Messages){ + interaction.member.send({content:currentTicketOptions.message}) + } + } + catch{console.log("[system] can't send DM to member, member doesn't allow dm's")} + + + + //update storage + ticketStorage.setItem(interaction.member.id,Number(ticketStorage.getItem(interaction.member.id))+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 = [] + + //set everyone allowed + if (config.system['has@everyoneaccess']){ + var everyoneAllowPerms = ["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] + var everyoneDenyPerms = [] + }else{ + var everyoneAllowPerms = [] + var everyoneDenyPerms = ["VIEW_CHANNEL"] + } + permissionsArray.push({ + id:interaction.guild.id, + type:"role", + allow:everyoneAllowPerms, + deny:everyoneDenyPerms + }) + + //add the user that created the ticket + permissionsArray.push({ + id:interaction.member.id, + type:"member", + allow:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] + }) + + //add main adminroles + config.main_adminroles.forEach((role) => { + permissionsArray.push({ + id:role, + type:"role", + allow:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] + }) + }) + + //add ticket adminroles + /** + * @type {String[]} + */ + const ticketadmin = currentTicketOptions.adminroles + ticketadmin.forEach((role) => { + if (!config.main_adminroles.includes(role)){ + permissionsArray.push({ + id:role, + type:"role", + allow:["ADD_REACTIONS","ATTACH_FILES","EMBED_LINKS","SEND_MESSAGES","VIEW_CHANNEL"] + }) + } + }) + + //add member role + if (config.system.member_role != "" && config.system.member_role != " " && config.system.member_role != "false" && config.system.member_role != "null" && config.system.member_role != "0"){ + permissionsArray.push({ + id:config.system.member_role, + type:"role", + deny:["VIEW_CHANNEL"] + }) + } + + + //create the channel + interaction.guild.channels.create(ticketName,{ + type:"GUILD_TEXT", + parent:newTicketCategory, + reason:"A new ticket was created", + permissionOverwrites:permissionsArray + + }).then((ticketChannel) => { + userTicketStorage.setItem(ticketChannel.id,interaction.member.id) + + var ticketEmbed = new discord.MessageEmbed() + .setColor(config.main_color) + .setTitle("You created a ticket!") + .setDescription("The staff will help you soon!\n\n*Click on the button below to close the ticket!*") + + ticketChannel.send({ + content:"<@"+interaction.member.id+"> @everyone", + embeds:[ticketEmbed], + components:[closeButton] + + }).then(firstmsg => { + firstmsg.pin() + }) + if (config.logs){console.log("[system] created a new ticket (name:"+logsname+",user:"+interaction.user.username+")")} + }) + }else{ + try { + if (config.system.enable_DM_Messages){ + interaction.member.send("you already have the maximum number of tickets allowed!") + } + } + catch{console.log("[system] can't send DM to member, member doesn't allow dm's")} + + + } + } + }) +} \ No newline at end of file diff --git a/core/transcript.js b/core/transcript.js new file mode 100644 index 0000000..475b33a --- /dev/null +++ b/core/transcript.js @@ -0,0 +1,36 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = require("../config.json") + +/** + * + * @param {discord.Message[]} messagecollection + * @returns new `discord.messageAttachment` + */ + +exports.createTranscript = async (messagecollection) => { + try { + const filearray = ["TRANSCRIPT START:"] + + messagecollection.reverse() + + + messagecollection.forEach((msg) => { + const timestamp = new Date(msg.createdTimestamp) + + if (msg.content){var content = msg.content}else{var content = "*empty message*"} + + const msgstats = "files: "+msg.attachments.size+" | embeds: "+msg.embeds.length+" | isbot: "+msg.author.bot + filearray.push("["+timestamp.getDate()+"/"+timestamp.getMonth()+"/"+timestamp.getFullYear()+" | "+timestamp.getHours()+":"+timestamp.getMinutes()+":"+timestamp.getSeconds()+" | "+msg.author.tag+" ] ["+msgstats+"]\n"+content+"\n") + }) + + if (filearray.length < 2) filearray.push("transcript is empty") + const filestring = filearray.join("\n") + const filebuff = Buffer.from(filestring,"utf-8") + return new discord.MessageAttachment(filebuff,"transcript.txt") + }catch(err){ + console.log(err) + return false + } +} \ No newline at end of file diff --git a/core/utils/getTicketMessages.js b/core/utils/getTicketMessages.js new file mode 100644 index 0000000..278afb7 --- /dev/null +++ b/core/utils/getTicketMessages.js @@ -0,0 +1,13 @@ +const config = require("../../config.json") +exports.getTicketMessage = (input) => { + var returned = config.messages.find(a => a.id == input) + if (returned){ + return returned + } + var result = config.messages[0] + if (!result){ + throw new Error("'messages' object in config.json is empty") + return + } + return result +} \ No newline at end of file diff --git a/documentation/CHANGELOG.md b/documentation/CHANGELOG.md deleted file mode 100644 index 28e9a93..0000000 --- a/documentation/CHANGELOG.md +++ /dev/null @@ -1,96 +0,0 @@ -# changelog - -## v1.3.2 -### added: -- an alias for !resetdatabase: !resetdb -- you need the "chalk" library now! -- checker.js -- "enabled" option for status -- better console logs -- enable logs option to config -### fixed -- a bug that crashed the bot on using the command !resetdatabase -- "member_role" in config is optional but not recommended -- fixed crash on startup -- fixed bug where the bot crashes by using transcripts -- changed package.json -### removed -- "canvas" library is not longer required -- removed "server_icon" & "server_name" from config - -## v1.3.1 -### added: -- Q&A in the README.md -- v1.4.0 announcement -### fixed -- a bug that happend when you created too much tickets -- deprecation warnings (setAuthor & setFooter) -- the logo in README.md is smaller now -- markdown files relocated -- fixed a bug that some messages appeared in Dutch -### removed -- nothing - -## v1.3.0 -### added: -- now its possible to create 6 tickets instead of 4 -- you can edit the number of tickets per user now -- you can edit & enable/disable all messages -- all embeds are now editable -- the color of the button can be changed -- updated config.json -- updated README.md -- a new type of button is here: website links -### fixed -- nothing at the moment :) -### removed -- nothing - -## v1.2.2 -### added: -- nothing -### fixed: -- a bug that caused the `warn "message" event deprecated` to be displayed every time you start the bot in the console. -- a visual bug that caused the categories from `!ticket msg` to be displayed in a wrong pattern. -### removed: -- nothing - -## v1.2.1 -### added: -- you can disable credits in config.json -### fixed: -- a bug that crashed the bot on startup -### removed -- nothing - -## v1.2.0 -### added: -- the bot is in english now -- you can choose your own ticket options now -- added !resetdatabase -- updated README.md -### fixed: -- the first bugs -### removed: -- some useless commands - -## v1.1.1 -### added: -- added "status" options in config.json -### fixed: -- fixed a bug where the bot's status could be "playing highmt". -### removed: -- nothing - -## v1.1.0 -### added: -- option to choose your own prefix -- enable/disable transcripts -- enable/disable categories -### fixed: -- the bot crashed sometimes -### removed: -- credits - -## v1.0.0 -- initial release \ No newline at end of file diff --git a/documentation/CONFIG.md b/documentation/CONFIG.md deleted file mode 100644 index 19da514..0000000 --- a/documentation/CONFIG.md +++ /dev/null @@ -1,68 +0,0 @@ -# config file -[![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-1.3.2%20stable-brightgreen.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/releases/tag/v1.3.2) -[![npm](https://img.shields.io/badge/npm-external%20libraries%20needed-CB3837.svg?style=flat-square&logo=npm)](#packages) -[![license](https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/blob/main/LICENSE) - -In this file you find all the information about commands in the config.json file - - -- bot_name ➜ the name from the bot _**type: string**_ -- main_color ➜ the color used in embeds _**type: hex color (#000000)**_ -- auth_token ➜ your bot's token _**type: string**_ -- botperms_role ➜ this role can do all admin commands from the bot _**type: discord role id**_ -- prefix ➜ the prefix _**type: string**_ -- logs ➜ enable logs _**type: boolean**_ -- credits ➜ disable the credits _**type: boolean**_ - -## status: (the status from the bot) -- enabled ➜ enable status _**type: boolean**_ -- type ➜ set the status type _**type: string (PLAYING | LISTENING | WATCHING | CUSTOM)** -- text ➜ set the status text _**type: string**_ - -## system: (the configuration for the ticket system) -- ticket_channel ➜ channel where you are going to set `!ticket msg` _**type: discord channel id**_ -- max_allowed_tickets ➜ the maximum allowed tickets per person _**type: number**_ -- enable_DM_Messages ➜ enable DM messages _**type: boolean**_ -- enable_category ➜ enable category _**type: boolean**_ -- ticket_category ➜ the category id _**type: discord category id**_ -- has@everyoneaccess ➜ when enabled everyone has acces to the tickets _**type: boolean**_ -- member_role ➜ this role doesn't have access to tickets _**type: discord role id**_ -- enable_transcript ➜ enable the transcript _**type: boolean**_ -- transcript_channel ➜ the transcript channel id _**type: discord channel id**_ - -## messages: (general, dm, ticket) -- nopermissions ➜ the message when someone doesn't have permissions _**type: string**_ -- alreadyCreated ➜ if you have too much tickets open _**type: string**_ -- newTicket ➜ when you create a new ticket _**type: string**_ -- closeTicket ➜ when you close a ticket _**type: string**_ -- newTicketEmbed ➜ the first message in the ticket channel _**type: string** - -## layout: (ticketEmbed, ticketMsg, transcript) -`transcript` doesn't have all the options - -- customColorEnabled ➜ custom embed color enabled _**type: boolean** -- customColor ➜ the custom color _**type: hex color (#000000)** -- footerEnabled ➜ custom footer enabled _**type: boolean** -- footer ➜ the custom footer _**type: string** -- thumbnailEnabled ➜ custom thumbnail enabled _**type: boolean** -- thumbnailURL ➜ the image _**type: string** - -## options: (per ticket) -color is not needed when `isURL` is `true` - -- enabled ➜ enable this ticket _**type: boolean** -- icon ➜ the button emoji _**type: emoji or custom emoji** -- description ➜ ticket description _**type: string** -- name ➜ ticket name _**type: string** -- channel_prefix ➜ ticket prefix _**type: string** -- color ➜ button color _**type: string (red|green|blue|gray|none)** -- isURL ➜ create link to a website instead of a normal ticket _**type: boolean** -- url ➜ the website to go to _**type: string** - -# thanks for using open-ticket -I didn't know that open-tickets was going to be so much used. I will be creating more updates in the future! - -If you have an idea for new updates you can always say it in my discord server! - -open-tickets v1.3.2 \ No newline at end of file diff --git a/documentation/Q&A.md b/documentation/Q&A.md deleted file mode 100644 index 9f221a2..0000000 --- a/documentation/Q&A.md +++ /dev/null @@ -1,26 +0,0 @@ -# Question & Answer -## How to install node.js 16? -You can install the latest version of node.js on their [website](https://nodejs.org/en/download/). You also need `npm`! - -## How to install all the npm packages? -If you installed node.js successful, you can run `npm install` in the command prompt and all the required modules will be installed. - -## Where is the config file documentation? -You can find it [here](...) - -**How to start the bot?** -If you successfully installed all the npm modules (see above), you can run `npm start` - -## Do I need to know discord.js? -Technically not, but it is useful to know if there is an error. - -## Why do I get this error? -**Invalid token:** - -_Your discord token is not correct, try to look in the config.json file if it's the correct token._ - -**Privileged intent provided is not enabled or whitelisted:** - -_You must enable all the **Gateaway Intents** in the developer panel in the "bot" tab_ - -### Report other errors to the discord server! \ No newline at end of file diff --git a/index.js b/index.js index 90515b7..8512b54 100644 --- a/index.js +++ b/index.js @@ -1,37 +1,67 @@ const discord = require('discord.js') const config = require('./config.json') const intents = discord.Intents -const client = new discord.Client({intents:[intents.FLAGS.GUILDS,intents.FLAGS.GUILD_MESSAGES,intents.FLAGS.GUILD_MEMBERS]}) +const client = new discord.Client({intents:[intents.FLAGS.GUILDS,intents.FLAGS.GUILD_MESSAGES,intents.FLAGS.GUILD_MEMBERS],partials:["CHANNEL"]}) exports.client = client -require("./checker")() +client.setMaxListeners(20) client.on('ready',async () => { const chalk = await (await import("chalk")).default - console.log(chalk.green("open-ticket ready!")) - if (config.logs){chalk.white("\n\nlogs:\n============")} - if (config.status.enabled){ - client.user.setActivity(config.status.text,{type:config.status.type}) + if (process.argv[2] != "slash"){ + console.log(chalk.green("open-ticket ready!")) + if (config.logs){console.log(chalk.white("\n\nlogs:\n============"))} + if (config.status.enabled){ + client.user.setActivity(config.status.text,{type:config.status.type}) + } + }else{ + console.log(chalk.red("STARTING IN ")+chalk.blue("SLASH MODE")+chalk.red("...")) + console.log("logs:\n================") + console.log("client logged in...") + console.log("loading files...") + if (process.argv[3] == "enable"){ + console.log(chalk.green("switching to slashEnable.js")) + require("./core/slashSystem/slashEnable")() + }else if (process.argv[3] == "disable"){ + console.log(chalk.green("switching to slashDisable.js")) + require("./core/slashSystem/slashDisable")() + }else{ + console.log(chalk.red("Internal Error: unknown slash mode!")) + process.exit(1) + } } }) -var storage = require('./storage/storage') -exports.TicketStorage = storage.ticketStorage -exports.userTicketStorage = storage.userTicketStorage -exports.transcriptStorage = storage.transcriptStorage -exports.ticketNumberStorage = storage.ticketNumberStorage +//checker.js must stand HERE +//before the "if slash" -var ticket = require('./commands/ticket') -ticket() +if (process.argv[2] != "slash"){ + var storage = require('./core/dynamicdatabase/storage') + exports.TicketStorage = storage.ticketStorage + exports.userTicketStorage = storage.userTicketStorage + exports.transcriptStorage = storage.transcriptStorage + exports.ticketNumberStorage = storage.ticketNumberStorage -var ticketSystem = require('./commands/ticketSystem') -ticketSystem() + //commands + require('./commands/ticket')() + require("./commands/help")() -var ticketExtra = require("./commands/ticketExtra") -ticketExtra() + //core + require('./core/ticketOpener')() + require("./core/ticketCloser").runThis() + require("./core/closebuttons")() -var database = require('./commands/database') -database() + /** + * We need: + * - checker.js + * - extracmds.js + */ +} + +process.on('unhandledRejection',async (error) => { + const chalk = await (await import("chalk")).default + console.log(chalk.red("ERROR: ")+error) +}) client.login(config.auth_token) \ No newline at end of file diff --git a/logo.png b/logo.png index e3c7fa6ec5c7da299063ca7e1b50325310a8519f..e6988c9ff5bd64b43d6f52d9fedf62ed21e2eeba 100644 GIT binary patch literal 139078 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

^Zx9)zDeQ!OJ7y^Ww$b`(eF75S!?sA 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

=rVcRc2IPFsa1CcINw()aiwmG^usf4% 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 Date: Mon, 2 May 2022 11:13:18 +0200 Subject: [PATCH 02/20] removed token :) --- .gitignore | 3 ++- commands/extracmds.js | 2 +- config.json | 37 +++++++++------------------ index.js | 8 +++++- originalConfig.json | 59 ------------------------------------------- 5 files changed, 22 insertions(+), 87 deletions(-) delete mode 100644 originalConfig.json diff --git a/.gitignore b/.gitignore index ccb2c80..3818e26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ -package-lock.json \ No newline at end of file +package-lock.json +devConfig.json \ No newline at end of file diff --git a/commands/extracmds.js b/commands/extracmds.js index 11c59c1..68060f1 100644 --- a/commands/extracmds.js +++ b/commands/extracmds.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config //============================= // NOT READY YET diff --git a/config.json b/config.json index baad7e3..82b13fb 100644 --- a/config.json +++ b/config.json @@ -1,9 +1,9 @@ { "bot_name":"Wumpus", "main_color":"#fffff", - "server_id":"935983011281903676", - "auth_token":"OTU4MjkwNjExMzMxNTM4OTc1.YkLLrQ.J9yfO7LgXplNahXv7xQNNvKG6fY", - "main_adminroles":["936153662668034058"], + "server_id":"server id (for slash cmds)", + "auth_token":"auth token", + "main_adminroles":["discord role id"], "prefix":"!ticket ", "logs":true, "languagefile":"english.json (currently not working)", @@ -16,16 +16,16 @@ }, "system":{ - "ticket_channel":"968899402020306994", + "ticket_channel":"channel id", "max_allowed_tickets":3, "enable_DM_Messages":true, "has@everyoneaccess":false, - "member_role":"936153574650544180", + "member_role":"member role (doesn't have access to tickets)", - "enable_transcript":true, - "enable_DM_transcript":true, - "transcript_channel":"968899606912061490" + "enable_transcript":false, + "enable_DM_transcript":false, + "transcript_channel":"channel id" }, "options":[ @@ -38,9 +38,9 @@ "type":"ticket", "color":"gray", - "adminroles":["936153350704091167"], + "adminroles":["role id"], "channelprefix":"question-", - "category":"935983011281903678", + "category":"category id", "message":"You created a ticket!", "enableDMMessage":true @@ -54,9 +54,9 @@ "type":"ticket", "color":"green", - "adminroles":["936153350704091167"], + "adminroles":["role id"], "channelprefix":"apply-", - "category":"935983011281903678", + "category":"category id", "message":"You created a ticket!", "enableDMMessage":true @@ -76,19 +76,6 @@ "options":["general","apply"], "enableTicketExplaination":true, "enableMaxTicketsWarning":true - }, - { - "id":"5", - "name":"sodfjiqdosmifj", - "description":"Youoidjfddiddjfmqoisdjf so customize all the embeds now!", - "enableFooter":true, - "footer":"Open Tiiiieakpeak", - "enableThumbnail":false, - "thumbnail":"https://www.example.com/catmemes/cat.png", - "color":"#0075e9", - "options":["apply"], - "enableTicketExplaination":true, - "enableMaxTicketsWarning":true } ] } \ No newline at end of file diff --git a/index.js b/index.js index 8512b54..ffa0265 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,17 @@ const discord = require('discord.js') -const config = require('./config.json') const intents = discord.Intents const client = new discord.Client({intents:[intents.FLAGS.GUILDS,intents.FLAGS.GUILD_MESSAGES,intents.FLAGS.GUILD_MEMBERS],partials:["CHANNEL"]}) exports.client = client client.setMaxListeners(20) +//change to FALSE on release +const isDev = true +//!!!!!!!!!!! + +if (isDev){const config = require('./devConfig.json')}else{const config = require('./config.json')} +exports.config = config + client.on('ready',async () => { const chalk = await (await import("chalk")).default if (process.argv[2] != "slash"){ diff --git a/originalConfig.json b/originalConfig.json deleted file mode 100644 index fdfeb43..0000000 --- a/originalConfig.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "bot_name":"Wumpus", - "main_color":"#fffff", - "server_id":"the server id (for slash commands)", - "auth_token":"the bot's auth token", - "main_adminroles":["the id's from admin roles"], - "prefix":"!", - "logs":true, - "languagefile":"english.json (currently not working)", - - "credits":true, - "status":{ - "enabled":true, - "type":"PLAYING | LISTENING | WATCHING | CUSTOM", - "text":"!ticket" - }, - - "system":{ - "ticket_channel":"id of the channel with the !ticket msg", - "max_allowed_tickets":5, - "enable_DM_Messages":true, - - "has@everyoneaccess":false, - "member_role":"this role doesn't have access to tickets", - - "enable_transcript":false, - "enable_DM_transcript":false, - "transcript_channel":"the channel for transcripts" - }, - - "options":[ - { - "id":"ticket1", - "name":"Test Ticket", - "description":"Create a general ticket.", - "icon":"🎫", - "label":"i'm a ticket button", - "type":"ticket", - - "color":"red|green|blue|gray|none", - "adminroles":["role id array"], - "channelprefix":"support-", - "category":"discord category id", - "message":"this is send in DM", - "enableDMMessage":true - - } - ], - - "messages":[ - { - "id":"1", - "name":"Choose Your Ticket", - "description":"You can click one of the buttons below!", - "color":"#ffffff", - "options":["ticket1","ticket2"] - } - ] -} \ No newline at end of file From 4e736260721e3c772fb118e35f4e358a6050c2d9 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 11:20:04 +0200 Subject: [PATCH 03/20] created devConfig.json --- README.md | 2 +- commands/help.js | 2 +- commands/ticket.js | 2 +- core/closebuttons.js | 2 +- core/getoptions.js | 3 ++- core/slashSystem/slashDisable.js | 3 ++- core/slashSystem/slashEnable.js | 3 ++- core/ticketCloser.js | 2 +- core/ticketMessageEmbed.js | 2 +- core/ticketOpener.js | 2 +- core/transcript.js | 2 +- core/utils/getTicketMessages.js | 3 ++- index.js | 2 +- storage/userTickets/970615545403678750 | 1 + 14 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 storage/userTickets/970615545403678750 diff --git a/README.md b/README.md index 0bd2073..00e6a32 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # WARNING: this is a pre-version The version above is unstable so if you find bugs, please report them in the discord server -[click here for a stable version](https://www.github.com/DJj123dj/open-ticket/releases/tab/v1.3.2) +[click here for a stable version](https://www.github.com/DJj123dj/open-ticket/releases/tab/v1.3.2a) # open-ticket diff --git a/commands/help.js b/commands/help.js index 9608613..38fd500 100644 --- a/commands/help.js +++ b/commands/help.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config module.exports = () => { const helpEmbed = new discord.MessageEmbed() diff --git a/commands/ticket.js b/commands/ticket.js index fce17e7..f76af3c 100644 --- a/commands/ticket.js +++ b/commands/ticket.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config /** ============================= ** NOT READY YET diff --git a/core/closebuttons.js b/core/closebuttons.js index 0cd6ea9..a771278 100644 --- a/core/closebuttons.js +++ b/core/closebuttons.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config module.exports = () => { diff --git a/core/getoptions.js b/core/getoptions.js index 60ff236..a8a7744 100644 --- a/core/getoptions.js +++ b/core/getoptions.js @@ -1,4 +1,5 @@ -const config = require("../config.json") +const bot = require("../index") +const config = bot.config /** * @param {Number} index diff --git a/core/slashSystem/slashDisable.js b/core/slashSystem/slashDisable.js index 0eef12a..e89a254 100644 --- a/core/slashSystem/slashDisable.js +++ b/core/slashSystem/slashDisable.js @@ -1,6 +1,7 @@ const discord = require("discord.js") +const bot = require("../../index") const client = require("../../index").client -const config = require("../../config.json") +const config = bot.config const getoptions = require("../getoptions") module.exports = async () => { diff --git a/core/slashSystem/slashEnable.js b/core/slashSystem/slashEnable.js index fd0e448..e91d5c0 100644 --- a/core/slashSystem/slashEnable.js +++ b/core/slashSystem/slashEnable.js @@ -1,6 +1,7 @@ const discord = require("discord.js") +const bot = require("../../index") const client = require("../../index").client -const config = require("../../config.json") +const config = bot.config const getoptions = require("../getoptions") module.exports = async () => { diff --git a/core/ticketCloser.js b/core/ticketCloser.js index ffbd234..2e4c307 100644 --- a/core/ticketCloser.js +++ b/core/ticketCloser.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config const ticketStorage = bot.TicketStorage const userTicketStorage = bot.userTicketStorage diff --git a/core/ticketMessageEmbed.js b/core/ticketMessageEmbed.js index 2d85bee..f681180 100644 --- a/core/ticketMessageEmbed.js +++ b/core/ticketMessageEmbed.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config const getOptions = require("./getoptions") const getbyId = getOptions.getOptionsById diff --git a/core/ticketOpener.js b/core/ticketOpener.js index 81b3dfb..ea2d4d2 100644 --- a/core/ticketOpener.js +++ b/core/ticketOpener.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config const getconfigoptions = require("./getoptions") diff --git a/core/transcript.js b/core/transcript.js index 475b33a..61a0dd0 100644 --- a/core/transcript.js +++ b/core/transcript.js @@ -1,7 +1,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client -const config = require("../config.json") +const config = bot.config /** * diff --git a/core/utils/getTicketMessages.js b/core/utils/getTicketMessages.js index 278afb7..22ed608 100644 --- a/core/utils/getTicketMessages.js +++ b/core/utils/getTicketMessages.js @@ -1,4 +1,5 @@ -const config = require("../../config.json") +const bot = require("../../index") +const config = bot.config exports.getTicketMessage = (input) => { var returned = config.messages.find(a => a.id == input) if (returned){ diff --git a/index.js b/index.js index ffa0265..c7bc453 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,7 @@ client.setMaxListeners(20) const isDev = true //!!!!!!!!!!! -if (isDev){const config = require('./devConfig.json')}else{const config = require('./config.json')} +if (isDev){var config = require('./devConfig.json')}else{var config = require('./config.json')} exports.config = config client.on('ready',async () => { diff --git a/storage/userTickets/970615545403678750 b/storage/userTickets/970615545403678750 new file mode 100644 index 0000000..71d7104 --- /dev/null +++ b/storage/userTickets/970615545403678750 @@ -0,0 +1 @@ +779742674932072469 \ No newline at end of file From 92c1e8fce0e9e5a8a7be1877e770a36c2bb0f354 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 11:21:45 +0200 Subject: [PATCH 04/20] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3818e26..77b939d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ package-lock.json -devConfig.json \ No newline at end of file +devConfig.json +.DS_Store \ No newline at end of file From 8e7a98b81098bfb3bac0e0e8bcec1b9260103917 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 11:35:41 +0200 Subject: [PATCH 05/20] created cleardatabase system --- core/dynamicdatabase/cleardata.js | 28 ++++++++++++++++++++++++++ package.json | 3 ++- storage/tickets/779742674932072469 | 1 - storage/tickets/858640346892337163 | 1 - storage/userTickets/968902556971585536 | 1 - storage/userTickets/968902650873675838 | 1 - storage/userTickets/968903429693001779 | 1 - storage/userTickets/968903924390174790 | 1 - storage/userTickets/968913030110904340 | 1 - storage/userTickets/968915615333093387 | 1 - storage/userTickets/968917301288108072 | 1 - storage/userTickets/968920379974373436 | 1 - storage/userTickets/968920740558684210 | 1 - storage/userTickets/968921182877388930 | 1 - storage/userTickets/968921695505223721 | 1 - storage/userTickets/968922637596258304 | 1 - storage/userTickets/968922760787161098 | 1 - storage/userTickets/968923098571239474 | 1 - storage/userTickets/969289034599919666 | 1 - storage/userTickets/969289151692288100 | 1 - storage/userTickets/969289430370230272 | 1 - storage/userTickets/969289687808241746 | 1 - storage/userTickets/969289892662235188 | 1 - storage/userTickets/969290103337938966 | 1 - storage/userTickets/969290783716941914 | 1 - storage/userTickets/970222239867871272 | 1 - storage/userTickets/970581087891886080 | 1 - storage/userTickets/970581271459799040 | 1 - storage/userTickets/970615545403678750 | 1 - 29 files changed, 30 insertions(+), 28 deletions(-) create mode 100644 core/dynamicdatabase/cleardata.js delete mode 100644 storage/tickets/779742674932072469 delete mode 100644 storage/tickets/858640346892337163 delete mode 100644 storage/userTickets/968902556971585536 delete mode 100644 storage/userTickets/968902650873675838 delete mode 100644 storage/userTickets/968903429693001779 delete mode 100644 storage/userTickets/968903924390174790 delete mode 100644 storage/userTickets/968913030110904340 delete mode 100644 storage/userTickets/968915615333093387 delete mode 100644 storage/userTickets/968917301288108072 delete mode 100644 storage/userTickets/968920379974373436 delete mode 100644 storage/userTickets/968920740558684210 delete mode 100644 storage/userTickets/968921182877388930 delete mode 100644 storage/userTickets/968921695505223721 delete mode 100644 storage/userTickets/968922637596258304 delete mode 100644 storage/userTickets/968922760787161098 delete mode 100644 storage/userTickets/968923098571239474 delete mode 100644 storage/userTickets/969289034599919666 delete mode 100644 storage/userTickets/969289151692288100 delete mode 100644 storage/userTickets/969289430370230272 delete mode 100644 storage/userTickets/969289687808241746 delete mode 100644 storage/userTickets/969289892662235188 delete mode 100644 storage/userTickets/969290103337938966 delete mode 100644 storage/userTickets/969290783716941914 delete mode 100644 storage/userTickets/970222239867871272 delete mode 100644 storage/userTickets/970581087891886080 delete mode 100644 storage/userTickets/970581271459799040 delete mode 100644 storage/userTickets/970615545403678750 diff --git a/core/dynamicdatabase/cleardata.js b/core/dynamicdatabase/cleardata.js new file mode 100644 index 0000000..fed8e15 --- /dev/null +++ b/core/dynamicdatabase/cleardata.js @@ -0,0 +1,28 @@ +const fs = require('fs') + +const dataPaths = ["./storage/tickets","./storage/transcripts","./storage/userTickets"] + +var index = 0 + +dataPaths.forEach((path) => { + try { + fs.rmdirSync(path,{recursive:true,force:true}) + }catch{ + console.log(path,"doesn't exist") + } + setTimeout(() => { + try{ + fs.mkdirSync(path) + }catch{ + console.log(path,"already exists") + } + index++ + },500) +}) + +setInterval(() => { + if (index >= 3){ + console.log("All local data is cleared!") + process.exit(1) + } +},500) \ No newline at end of file diff --git a/package.json b/package.json index 8cc3974..3042ea9 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1", "slashEnable":"node index.js slash enable", - "slashDisable":"node index.js slash disable" + "slashDisable":"node index.js slash disable", + "cleardata":"node ./core/dynamicdatabase/cleardata.js" }, "author": "DJdj Development", "license": "ISC", diff --git a/storage/tickets/779742674932072469 b/storage/tickets/779742674932072469 deleted file mode 100644 index 56a6051..0000000 --- a/storage/tickets/779742674932072469 +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/storage/tickets/858640346892337163 b/storage/tickets/858640346892337163 deleted file mode 100644 index c227083..0000000 --- a/storage/tickets/858640346892337163 +++ /dev/null @@ -1 +0,0 @@ -0 \ No newline at end of file diff --git a/storage/userTickets/968902556971585536 b/storage/userTickets/968902556971585536 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968902556971585536 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968902650873675838 b/storage/userTickets/968902650873675838 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968902650873675838 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968903429693001779 b/storage/userTickets/968903429693001779 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968903429693001779 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968903924390174790 b/storage/userTickets/968903924390174790 deleted file mode 100644 index ca9aa59..0000000 --- a/storage/userTickets/968903924390174790 +++ /dev/null @@ -1 +0,0 @@ -858640346892337163 \ No newline at end of file diff --git a/storage/userTickets/968913030110904340 b/storage/userTickets/968913030110904340 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968913030110904340 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968915615333093387 b/storage/userTickets/968915615333093387 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968915615333093387 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968917301288108072 b/storage/userTickets/968917301288108072 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968917301288108072 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968920379974373436 b/storage/userTickets/968920379974373436 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968920379974373436 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968920740558684210 b/storage/userTickets/968920740558684210 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968920740558684210 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968921182877388930 b/storage/userTickets/968921182877388930 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968921182877388930 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968921695505223721 b/storage/userTickets/968921695505223721 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968921695505223721 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968922637596258304 b/storage/userTickets/968922637596258304 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968922637596258304 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968922760787161098 b/storage/userTickets/968922760787161098 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968922760787161098 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/968923098571239474 b/storage/userTickets/968923098571239474 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/968923098571239474 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969289034599919666 b/storage/userTickets/969289034599919666 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969289034599919666 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969289151692288100 b/storage/userTickets/969289151692288100 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969289151692288100 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969289430370230272 b/storage/userTickets/969289430370230272 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969289430370230272 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969289687808241746 b/storage/userTickets/969289687808241746 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969289687808241746 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969289892662235188 b/storage/userTickets/969289892662235188 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969289892662235188 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969290103337938966 b/storage/userTickets/969290103337938966 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969290103337938966 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/969290783716941914 b/storage/userTickets/969290783716941914 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/969290783716941914 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/970222239867871272 b/storage/userTickets/970222239867871272 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/970222239867871272 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/970581087891886080 b/storage/userTickets/970581087891886080 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/970581087891886080 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/970581271459799040 b/storage/userTickets/970581271459799040 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/970581271459799040 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/970615545403678750 b/storage/userTickets/970615545403678750 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/970615545403678750 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file From 07de112f32ea7f788aae1ba627325ab2fbb75c92 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 12:41:15 +0200 Subject: [PATCH 06/20] added some new commands - rename - close - delete --- commands/close.js | 64 ++++++++++++++++++++++++++ commands/delete.js | 64 ++++++++++++++++++++++++++ commands/extracmds.js | 27 ----------- commands/help.js | 2 +- commands/rename.js | 81 +++++++++++++++++++++++++++++++++ core/closebuttons.js | 30 ++++++++++++ core/slashSystem/slashEnable.js | 23 ++++++++-- index.js | 3 ++ 8 files changed, 263 insertions(+), 31 deletions(-) create mode 100644 commands/close.js create mode 100644 commands/delete.js create mode 100644 commands/rename.js diff --git a/commands/close.js b/commands/close.js new file mode 100644 index 0000000..6e90c86 --- /dev/null +++ b/commands/close.js @@ -0,0 +1,64 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = bot.config + +module.exports = () => { + client.on("messageCreate",msg => { + if (!msg.content.startsWith(config.prefix+"close")) return + + + msg.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + msg.channel.send({content:"You are not in a ticket!"}) + return + } + + const closebutton = new discord.MessageActionRow() + .addComponents([ + new discord.MessageButton() + .setCustomId("closeTicketTrue1") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("🔒") + ]) + + msg.channel.send({content:"**Click on the button below to close this ticket!**",components:[closebutton]}) + + console.log("[system] closed a ticket via command") + + }) + + + }) + + client.on("interactionCreate",(interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "close") return + + interaction.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + interaction.reply({content:"You are not in a ticket!"}) + return + } + + const closebutton = new discord.MessageActionRow() + .addComponents([ + new discord.MessageButton() + .setCustomId("closeTicketTrue1") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("🔒") + ]) + + interaction.reply({content:"**Click on the button below to close this ticket!**",components:[closebutton]}) + + console.log("[system] closed a ticket via command") + + }) + }) +} \ No newline at end of file diff --git a/commands/delete.js b/commands/delete.js new file mode 100644 index 0000000..f0b7305 --- /dev/null +++ b/commands/delete.js @@ -0,0 +1,64 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = bot.config + +module.exports = () => { + client.on("messageCreate",msg => { + if (!msg.content.startsWith(config.prefix+"delete")) return + + + msg.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + msg.channel.send({content:"You are not in a ticket!"}) + return + } + + const closebutton = new discord.MessageActionRow() + .addComponents([ + new discord.MessageButton() + .setCustomId("deleteTicketTrue") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("❌") + ]) + + msg.channel.send({content:"**Click on the button below to delete this ticket!**",components:[closebutton]}) + + console.log("[system] deleted a ticket via command") + + }) + + + }) + + client.on("interactionCreate",(interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "delete") return + + interaction.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + interaction.reply({content:"You are not in a ticket!"}) + return + } + + const closebutton = new discord.MessageActionRow() + .addComponents([ + new discord.MessageButton() + .setCustomId("deleteTicketTrue") + .setDisabled(false) + .setStyle("SECONDARY") + .setEmoji("❌") + ]) + + interaction.reply({content:"**Click on the button below to delete this ticket!**",components:[closebutton]}) + + console.log("[system] deleted a ticket via command") + + }) + }) +} \ No newline at end of file diff --git a/commands/extracmds.js b/commands/extracmds.js index 68060f1..119d850 100644 --- a/commands/extracmds.js +++ b/commands/extracmds.js @@ -8,33 +8,6 @@ const config = bot.config //============================= module.exports = () => { - client.on("messageCreate",msg => { - var args = msg.content.split(" ") - if (args[0] == config.prefix+"ticket" && args[1] == "close"){ - - msg.channel.messages.fetchPinned().then(msglist => { - var firstmsg = msglist.last() - - if (firstmsg == undefined){ - msg.channel.send({content:"I didn't find the close button!"}) - } - if (firstmsg.author.id != client.user.id){ - msg.channel.send({content:"You are not in a ticket!"}) - return - } - var CloseMsg = new discord.MessageEmbed() - .setColor(config.main_color) - .setDescription("Click [here]("+firstmsg.url+") to close this ticket.") - .setTitle("Close this ticket:") - - msg.channel.send({embeds:[CloseMsg]}) - if (config.logs){console.log("[command] "+config.prefix+"ticket close (user:"+msg.author.username+")")} - }) - - - } - }) - client.on("messageCreate",msg => { var args = msg.content.split(" ") if (args[0] == config.prefix+"ticket" && args[1] == "rename"){ diff --git a/commands/help.js b/commands/help.js index 38fd500..c3b0848 100644 --- a/commands/help.js +++ b/commands/help.js @@ -18,7 +18,7 @@ module.exports = () => { if (!msg.content.startsWith(config.prefix)) return var args = msg.content.split(config.prefix) - 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("msg")){ + 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")){ msg.channel.send({embeds:[helpEmbed]}) if (config.logs){console.log("[command] "+config.prefix+"ticket help (user:"+msg.author.username+")")} diff --git a/commands/rename.js b/commands/rename.js new file mode 100644 index 0000000..e3333e4 --- /dev/null +++ b/commands/rename.js @@ -0,0 +1,81 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = bot.config + +module.exports = () => { + client.on("messageCreate",msg => { + if (!msg.content.startsWith(config.prefix+"rename")) return + + msg.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + msg.channel.send({content:"You are not in a ticket!"}) + return + } + + + if (!msg.member.permissions.has("MANAGE_CHANNELS") && !msg.member.permissions.has("ADMINISTRATOR")){ + return msg.channel.send({content:"You have no permissions to change the channel name!\nYou need the `ADMINISTRATOR` or `MANAGE_CHANNELS` permission"}) + } + + var newname = msg.content.split(config.prefix+"rename")[1].substring(1) + 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({content:"**The name is changed!**\nWarning: you can only change the channel name 2 times per minute! (this is due discord rate limits)"}) + + console.log("[system] renamed a ticket via command") + + }) + + + }) + + client.on("interactionCreate",(interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "rename") return + + interaction.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + interaction.reply({content:"You are not in a ticket!"}) + return + } + + const member = client.guilds.cache.find(g => g.id == interaction.guild.id).members.cache.find(m => m.id == interaction.member.id) + if (!member.permissions.has("MANAGE_CHANNELS") && !member.permissions.has("ADMINISTRATOR")){ + return interaction.reply({content:"You have no permissions to change the channel name!\nYou need the `ADMINISTRATOR` or `MANAGE_CHANNELS` permission"}) + } + + 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.reply({content:"**The name is changed!**\nWarning: you can only change the channel name 2 times per minute! (this is due discord rate limits)"}) + + console.log("[system] renamed a ticket via command") + + }) + }) +} \ No newline at end of file diff --git a/core/closebuttons.js b/core/closebuttons.js index a771278..43b9f3b 100644 --- a/core/closebuttons.js +++ b/core/closebuttons.js @@ -126,6 +126,36 @@ module.exports = () => { require("./ticketCloser").closeTicket(interaction,prefix,"close") }) + client.on("interactionCreate",interaction => { + if (!interaction.isButton()) return + if (interaction.customId != "closeTicketTrue1") return + + interaction.deferUpdate() + const closedButtonDisabled = new discord.MessageActionRow() + .addComponents([ + new discord.MessageButton() + .setCustomId("closeTicketTrue1") + .setDisabled(true) + .setStyle("SECONDARY") + .setEmoji("🔒") + ]) + interaction.message.edit({components:[closedButtonDisabled]}) + + /** + * @type {String} + */ + const name = interaction.channel.name + var prefix = "" + const tickets = config.options + tickets.forEach((ticket) => { + if (name.startsWith(ticket.channelprefix)){ + prefix = ticket.channelprefix + } + }) + + require("./ticketCloser").closeTicket(interaction,prefix,"close") + }) + //NORMAL DELETE client.on("interactionCreate",interaction => { diff --git a/core/slashSystem/slashEnable.js b/core/slashSystem/slashEnable.js index e91d5c0..df9e2c5 100644 --- a/core/slashSystem/slashEnable.js +++ b/core/slashSystem/slashEnable.js @@ -13,7 +13,6 @@ module.exports = async () => { var choices = [] ids.forEach((id) => { const option = getoptions.getOptionsById(id) - console.log(option.id) choices.push({name:option.name,value:option.id}) }) @@ -26,8 +25,8 @@ module.exports = async () => { var readystats = 0 setInterval(() => { - console.log("[status] there are "+chalk.blue(readystats+" out of 8")+" commands ready! (this can take some time)") - if (readystats >= 8){ + console.log("[status] there are "+chalk.blue(readystats+" out of 9")+" commands ready! (this can take some time)") + if (readystats >= 9){ console.log(chalk.green("ready!")) console.log(chalk.blue("you can now start the bot with 'npm start'!")) process.exit(1) @@ -160,4 +159,22 @@ module.exports = async () => { readystats++ }) + //rename + client.application.commands.create({ + name:"rename", + description:"Rename this ticket.", + defaultPermission:true, + type:"CHAT_INPUT", + options:[ + { + type:"STRING", + name:"name", + description:"The new name (without prefix).", + required:true + } + ] + },sid).then(() => { + readystats++ + }) + } \ No newline at end of file diff --git a/index.js b/index.js index c7bc453..87d49f2 100644 --- a/index.js +++ b/index.js @@ -51,6 +51,9 @@ if (process.argv[2] != "slash"){ //commands require('./commands/ticket')() require("./commands/help")() + require("./commands/close")() + require("./commands/delete")() + require("./commands/rename")() //core require('./core/ticketOpener')() From f163769de4ce87a3f9a55fa356248ada9173a4c7 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 14:54:52 +0200 Subject: [PATCH 07/20] updated cleardatabase with chalk --- core/closebuttons.js | 4 +- core/dynamicdatabase/cleardata.js | 75 ++++++++++++++++++++----------- core/ticketCloser.js | 7 ++- core/ticketOpener.js | 2 +- index.js | 8 ++-- package.json | 6 +-- 6 files changed, 66 insertions(+), 36 deletions(-) diff --git a/core/closebuttons.js b/core/closebuttons.js index 43b9f3b..1f1c903 100644 --- a/core/closebuttons.js +++ b/core/closebuttons.js @@ -67,7 +67,7 @@ module.exports = () => { .setDisabled(false) .setStyle("DANGER") .setLabel("Delete Ticket") - .setEmoji("❌") + .setEmoji("✖️") ) var closeRowClosed = new discord.MessageActionRow() .addComponents( @@ -76,7 +76,7 @@ module.exports = () => { .setDisabled(false) .setStyle("DANGER") .setLabel("Delete Ticket") - .setEmoji("❌") + .setEmoji("✖️") ) .addComponents( new discord.MessageButton() diff --git a/core/dynamicdatabase/cleardata.js b/core/dynamicdatabase/cleardata.js index fed8e15..a883d5a 100644 --- a/core/dynamicdatabase/cleardata.js +++ b/core/dynamicdatabase/cleardata.js @@ -1,28 +1,53 @@ const fs = require('fs') - -const dataPaths = ["./storage/tickets","./storage/transcripts","./storage/userTickets"] - -var index = 0 - -dataPaths.forEach((path) => { - try { - fs.rmdirSync(path,{recursive:true,force:true}) - }catch{ - console.log(path,"doesn't exist") - } - setTimeout(() => { - try{ - fs.mkdirSync(path) - }catch{ - console.log(path,"already exists") - } - index++ - },500) +const readline = require("readline") +const rl = readline.createInterface({ + input:process.stdin, + output:process.stdout, + terminal:true }) -setInterval(() => { - if (index >= 3){ - console.log("All local data is cleared!") - process.exit(1) - } -},500) \ No newline at end of file +const ask = async () => { + const chalk = await (await import("chalk")).default + rl.question(chalk.yellow("WARNING: are you sure that there are",chalk.red("no tickets open"),"at the moment? ")+chalk.green("(yes/no)")+"\n",(answer) => { + if (answer != "1" && !answer.toLowerCase().startsWith("yes")){ + console.log(chalk.red("invalid answer")) + ask() + return + } + console.log(chalk.green("clearing data...")) + runClear() + rl.close() + }) +} +ask() + +const runClear = async () => { + const chalk = await (await import("chalk")).default + console.log(chalk.blue("You can get a node.js warning below, just ignore that, it's not important!")) + const dataPaths = ["./storage/tickets","./storage/transcripts","./storage/userTickets"] + + var index = 0 + + dataPaths.forEach((path) => { + try { + fs.rmdirSync(path,{recursive:true,force:true}) + }catch{ + console.log(path,"doesn't exist") + } + setTimeout(() => { + try{ + fs.mkdirSync(path) + }catch{ + console.log(path,"already exists") + } + index++ + },500) + }) + + setInterval(() => { + if (index >= 3){ + console.log(chalk.bgGreen("All local data is cleared!")) + process.exit(1) + } + },500) +} \ No newline at end of file diff --git a/core/ticketCloser.js b/core/ticketCloser.js index 2e4c307..fad6168 100644 --- a/core/ticketCloser.js +++ b/core/ticketCloser.js @@ -14,6 +14,7 @@ const transcriptStorage = bot.transcriptStorage * @param {"delete"|"close"|"deletenotranscript"} mode */ exports.closeTicket = async (interaction,prefix,mode) => { + const chalk = await (await import("chalk")).default const channelmessages = await interaction.channel.messages.fetch() channelmessages.sweep((msgSweep) => { @@ -29,6 +30,10 @@ exports.closeTicket = async (interaction,prefix,mode) => { var getuserID = userTicketStorage.getItem(interaction.channel.id) var getusernameStep1 = client.users.cache.find(u => u.id === getuserID) + if (!getusernameStep1){ + console.log(chalk.red("[database error]"),"something went wrong when getting the data in the database.\nNo panic, this error fixes itself!") + } + var enableTranscript = true if (mode == "delete"){ @@ -61,7 +66,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { .setDisabled(false) .setStyle("DANGER") .setLabel("Delete Ticket") - .setEmoji("❌") + .setEmoji("✖️") ) .addComponents( new discord.MessageButton() diff --git a/core/ticketOpener.js b/core/ticketOpener.js index ea2d4d2..6bfcc31 100644 --- a/core/ticketOpener.js +++ b/core/ticketOpener.js @@ -25,7 +25,7 @@ module.exports = () => { .setDisabled(false) .setStyle("DANGER") .setLabel("Delete Ticket") - .setEmoji("❌") + .setEmoji("✖️") ) //ticket button click / create ticket diff --git a/index.js b/index.js index 87d49f2..5cb6e3e 100644 --- a/index.js +++ b/index.js @@ -68,9 +68,9 @@ if (process.argv[2] != "slash"){ } -process.on('unhandledRejection',async (error) => { - const chalk = await (await import("chalk")).default - console.log(chalk.red("ERROR: ")+error) -}) +//process.on('unhandledRejection',async (error) => { +// const chalk = await (await import("chalk")).default +// console.log(chalk.red("ERROR: ")+error) +//}) client.login(config.auth_token) \ No newline at end of file diff --git a/package.json b/package.json index 3042ea9..7b11fa2 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1", - "slashEnable":"node index.js slash enable", - "slashDisable":"node index.js slash disable", - "cleardata":"node ./core/dynamicdatabase/cleardata.js" + "slashEnable": "node index.js slash enable", + "slashDisable": "node index.js slash disable", + "cleardata": "node ./core/dynamicdatabase/cleardata.js" }, "author": "DJdj Development", "license": "ISC", From a9770ec66c5dd1053672473d2ca4d0128d9812db Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Mon, 2 May 2022 15:02:27 +0200 Subject: [PATCH 08/20] Update slashEnable.js --- core/slashSystem/slashEnable.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/slashSystem/slashEnable.js b/core/slashSystem/slashEnable.js index df9e2c5..7fbb6bb 100644 --- a/core/slashSystem/slashEnable.js +++ b/core/slashSystem/slashEnable.js @@ -24,14 +24,16 @@ module.exports = async () => { var readystats = 0 + process.stdout.write("[status] there are "+chalk.blue("0 out of 9")+" commands ready! (this can take up to 40 seconds)") setInterval(() => { - console.log("[status] there are "+chalk.blue(readystats+" out of 9")+" commands ready! (this can take some time)") + process.stdout.cursorTo(0) + process.stdout.write("[status] there are "+chalk.blue(readystats+" out of 9")+" commands ready! (this can take up to 40 seconds)") if (readystats >= 9){ - console.log(chalk.green("ready!")) - console.log(chalk.blue("you can now start the bot with 'npm start'!")) + console.log(chalk.green("\nready!")) + console.log(chalk.bgBlue("you can now start the bot with 'npm start'!")) process.exit(1) } - },1000) + },100) //create a ticket (/new or /ticket) client.application.commands.create({ From 130fd98d0c1546899a3377a4ab4ddad2ca88c8f8 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 15:26:40 +0200 Subject: [PATCH 09/20] bug fix fixed not starting up because of a temporary config file --- index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 5cb6e3e..d3013cb 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,14 @@ client.setMaxListeners(20) const isDev = true //!!!!!!!!!!! -if (isDev){var config = require('./devConfig.json')}else{var config = require('./config.json')} +if (isDev){ + try { + var config = require('./devConfig.json') + }catch{ + console.log("devConfig.json not found! Trying the normal config...") + var config = require("./config.json") + } +}else{var config = require('./config.json')} exports.config = config client.on('ready',async () => { From fa8741aef8e094c38a73d854232d49b6fb5adecc Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 18:00:40 +0200 Subject: [PATCH 10/20] test discord webhook --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index d3013cb..021f287 100644 --- a/index.js +++ b/index.js @@ -8,6 +8,7 @@ client.setMaxListeners(20) //change to FALSE on release const isDev = true //!!!!!!!!!!! +//test for discord webhook if (isDev){ try { From 14ad1f48f38d8898a112200e7f4bd9c38c63cbcd Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 18:03:35 +0200 Subject: [PATCH 11/20] test discord webhook --- index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/index.js b/index.js index 021f287..d3013cb 100644 --- a/index.js +++ b/index.js @@ -8,7 +8,6 @@ client.setMaxListeners(20) //change to FALSE on release const isDev = true //!!!!!!!!!!! -//test for discord webhook if (isDev){ try { From 3c2f39df9cd51c1bf67350d7b615aa26c4c909fd Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 18:08:09 +0200 Subject: [PATCH 12/20] Update README.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 00e6a32..5118e8f 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,7 @@ The version above is unstable so if you find bugs, please report them in the dis # open-ticket -[![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-1.4.0%20unstable-red.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/releases/tag/v1.4.0) -[![npm](https://img.shields.io/badge/npm-external%20libraries%20needed-CB3837.svg?style=flat-square&logo=npm)](#packages) -[![license](https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/blob/main/LICENSE) +[![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-1.4.0%20unstable-red.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/releases/tag/v1.4.0) [![npm](https://img.shields.io/badge/npm-external%20libraries%20needed-CB3837.svg?style=flat-square&logo=npm)](#packages) [![license](https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/blob/main/LICENSE) This is an open-source discord ticket bot, you can configure it and it comes with cool features like transcripts & custom options! From eddbcf9acdbf03e806419407e8f2adb0124da743 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 18:11:11 +0200 Subject: [PATCH 13/20] Update TEMPDOCS.md --- TEMPDOCS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TEMPDOCS.md b/TEMPDOCS.md index 57eaa2e..334fb38 100644 --- a/TEMPDOCS.md +++ b/TEMPDOCS.md @@ -1,2 +1,5 @@ # documentation -The documentation is not available here, go to the [wiki](https://github.com/DJj123dj/open-ticket/wiki) instead. (the wiki isn't fully done yet!) \ No newline at end of file +The documentation is not available here, go to the [wiki](https://github.com/DJj123dj/open-ticket/wiki) instead. (the wiki isn't fully done yet!) + +## 1.4.0 beta +There is currently no documentation available for open-ticket v1.4.0 beta. So if you want to test it, you need to figure it out on yourself at the moment! \ No newline at end of file From e3e5c04096acbe2a0c44d6ede1415495b8cf2f21 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 18:42:33 +0200 Subject: [PATCH 14/20] added the "add" & "remove" commands --- commands/add.js | 65 ++++++++++++++++++++++++++ commands/extracmds.js | 62 ------------------------ commands/remove.js | 65 ++++++++++++++++++++++++++ index.js | 2 + storage/tickets/779742674932072469 | 1 + storage/userTickets/971449683379646526 | 1 + 6 files changed, 134 insertions(+), 62 deletions(-) create mode 100644 commands/add.js create mode 100644 commands/remove.js create mode 100644 storage/tickets/779742674932072469 create mode 100644 storage/userTickets/971449683379646526 diff --git a/commands/add.js b/commands/add.js new file mode 100644 index 0000000..404d198 --- /dev/null +++ b/commands/add.js @@ -0,0 +1,65 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = bot.config + +module.exports = () => { + client.on("messageCreate",msg => { + if (!msg.content.startsWith(config.prefix+"add")) return + var user = msg.mentions.users.first() + if (!user){ + return msg.channel.send({content:"**There is no user to add:**\nuse: `"+config.prefix+"add `"}) + } + + if (!msg.member.permissions.has("MANAGE_CHANNELS") && !msg.member.permissions.has("ADMINISTRATOR")){ + return msg.channel.send({content:"You have no permissions to add someone to the channel!\nYou need the `ADMINISTRATOR` or `MANAGE_CHANNELS` permission"}) + } + + msg.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + msg.channel.send({content:"You are not in a ticket!"}) + return + } + + msg.channel.permissionOverwrites.create(user.id, { VIEW_CHANNEL:true, ADD_REACTIONS:true,ATTACH_FILES:true, EMBED_LINKS:true, SEND_MESSAGES:true}) + msg.channel.send({content:"Added "+user.tag+" to the ticket"}) + if (config.logs){console.log("[system] added user to ticket (name:"+user.username+",ticket:"+msg.channel.name+")")} + + var loguser = msg.mentions.users.first() + if (config.logs){console.log("[command] "+config.prefix+"add "+loguser.username+" (user:"+msg.author.username+")")} + }) + + }) + + client.on("interactionCreate",(interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "add") return + const user = interaction.options.getUser("user") + const member = client.guilds.cache.find(g => g.id == interaction.guild.id).members.cache.find(m => m.id == interaction.member.id) + + if (!member.permissions.has("MANAGE_CHANNELS") && !member.permissions.has("ADMINISTRATOR")){ + return interaction.reply({content:"You have no permissions to add someone to the channel!\nYou need the `ADMINISTRATOR` or `MANAGE_CHANNELS` permission"}) + } + + + interaction.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + interaction.reply({content:"You are not in a ticket!"}) + return + } + + interaction.channel.permissionOverwrites.create(user.id, { VIEW_CHANNEL:true, ADD_REACTIONS:true,ATTACH_FILES:true, EMBED_LINKS:true, SEND_MESSAGES:true}) + interaction.reply({content:"Added "+user.tag+" to the ticket"}) + if (config.logs){console.log("[system] added user to ticket (name:"+user.username+",ticket:"+interaction.channel.name+")")} + + var loguser = user + if (config.logs){console.log("[command] "+config.prefix+"add "+loguser.username+" (user:"+interaction.user.username+")")} + }) + + + }) +} \ No newline at end of file diff --git a/commands/extracmds.js b/commands/extracmds.js index 119d850..0a63230 100644 --- a/commands/extracmds.js +++ b/commands/extracmds.js @@ -8,68 +8,6 @@ const config = bot.config //============================= module.exports = () => { - client.on("messageCreate",msg => { - var args = msg.content.split(" ") - if (args[0] == config.prefix+"ticket" && args[1] == "rename"){ - - if (msg.member.roles.cache.has(config.botperms_role) == false && msg.author.id != "779742674932072469"){ - msg.channel.send({content:config.messages.general.nopermissions}) - return - } - - if (args[2] == null || args[2] == undefined || args[2] == "" || args[2] == false){ - msg.channel.send({content:"Not enough parameters!"}) - return - } - - var name = args[2] - var oldname = msg.channel.name - msg.channel.messages.fetchPinned().then(msglist => { - if (msglist.last().author.id == client.user.id){ - msg.channel.send({content:"The name from the ticket is changed!"}).then(rmsg => { - rmsg.channel.setName(name) - if (config.logs){console.log("[system] renamed a ticket (name:"+oldname+",newname:"+name+")")} - }) - }else{ - msg.channel.send({content:"You are not in a ticket!"}) - } - }) - if (config.logs){console.log("[command] "+config.prefix+"ticket rename (user:"+msg.author.username+")")} - } - }) - - client.on("messageCreate",msg => { - var args = msg.content.split(" ") - if (args[0] == config.prefix+"ticket" && args[1] == "add"){ - - if (msg.member.roles.cache.has(config.botperms_role) == false && msg.author.id != "779742674932072469"){ - msg.channel.send({content:config.messages.general.nopermissions}) - return - } - - msg.channel.messages.fetch().then(msglist => { - if (msglist.last().author.id != client.user.id){ - msg.channel.send({content:"You are not in a ticket!"}) - return - } - - if (args[2] == null || args[2] == undefined || args[2] == "" || args[2] == false){ - msg.channel.send({content:"Not enough parameters!"}) - return - } - var user = msg.mentions.users.first() - if (!user){ - return - } - - msg.channel.permissionOverwrites.create(user.id, { VIEW_CHANNEL:true, ADD_REACTIONS:true,ATTACH_FILES:true, EMBED_LINKS:true, SEND_MESSAGES:true}) - if (config.logs){console.log("[system] added user to ticket (name:"+user.username+",ticket:"+msg.channel.name+")")} - - }) - var loguser = msg.mentions.users.first() - if (config.logs){console.log("[command] "+config.prefix+"ticket add "+loguser.username+" (user:"+msg.author.username+")")} - } - }) client.on("messageCreate",msg => { var args = msg.content.split(" ") diff --git a/commands/remove.js b/commands/remove.js new file mode 100644 index 0000000..7f15655 --- /dev/null +++ b/commands/remove.js @@ -0,0 +1,65 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = bot.config + +module.exports = () => { + client.on("messageCreate",msg => { + if (!msg.content.startsWith(config.prefix+"remove")) return + var user = msg.mentions.users.first() + if (!user){ + return msg.channel.send({content:"**There is no user to remove:**\nuse: `"+config.prefix+"remove `"}) + } + + if (!msg.member.permissions.has("MANAGE_CHANNELS") && !msg.member.permissions.has("ADMINISTRATOR")){ + return msg.channel.send({content:"You have no permissions to remove someone from the channel!\nYou need the `ADMINISTRATOR` or `MANAGE_CHANNELS` permission"}) + } + + msg.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + msg.channel.send({content:"You are not in a ticket!"}) + return + } + + msg.channel.permissionOverwrites.delete(user.id) + msg.channel.send({content:"Deleted "+user.tag+" from the ticket"}) + if (config.logs){console.log("[system] deleted user from ticket (name:"+user.username+",ticket:"+msg.channel.name+")")} + + var loguser = msg.mentions.users.first() + if (config.logs){console.log("[command] "+config.prefix+"remove "+loguser.username+" (user:"+msg.author.username+")")} + }) + + }) + + client.on("interactionCreate",(interaction) => { + if (!interaction.isCommand()) return + if (interaction.commandName != "remove") return + const user = interaction.options.getUser("user") + const member = client.guilds.cache.find(g => g.id == interaction.guild.id).members.cache.find(m => m.id == interaction.member.id) + + if (!member.permissions.has("MANAGE_CHANNELS") && !member.permissions.has("ADMINISTRATOR")){ + return interaction.reply({content:"You have no permissions to remove someone from the channel!\nYou need the `ADMINISTRATOR` or `MANAGE_CHANNELS` permission"}) + } + + + interaction.channel.messages.fetchPinned().then(msglist => { + var firstmsg = msglist.last() + + if (firstmsg == undefined || firstmsg.author.id != client.user.id){ + interaction.reply({content:"You are not in a ticket!"}) + return + } + + interaction.channel.permissionOverwrites.delete(user.id) + interaction.reply({content:"Deleted "+user.tag+" from the ticket"}) + if (config.logs){console.log("[system] removed user from ticket (name:"+user.username+",ticket:"+interaction.channel.name+")")} + + var loguser = user + if (config.logs){console.log("[command] "+config.prefix+"remove "+loguser.username+" (user:"+interaction.user.username+")")} + }) + + + }) +} \ No newline at end of file diff --git a/index.js b/index.js index d3013cb..8697d34 100644 --- a/index.js +++ b/index.js @@ -61,6 +61,8 @@ if (process.argv[2] != "slash"){ require("./commands/close")() require("./commands/delete")() require("./commands/rename")() + require("./commands/add")() + require("./commands/remove")() //core require('./core/ticketOpener')() diff --git a/storage/tickets/779742674932072469 b/storage/tickets/779742674932072469 new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/storage/tickets/779742674932072469 @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/storage/userTickets/971449683379646526 b/storage/userTickets/971449683379646526 new file mode 100644 index 0000000..71d7104 --- /dev/null +++ b/storage/userTickets/971449683379646526 @@ -0,0 +1 @@ +779742674932072469 \ No newline at end of file From 4cc12eb727efeb169bd1b2867c289970566a7d50 Mon Sep 17 00:00:00 2001 From: Jasper <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 19:02:20 +0200 Subject: [PATCH 15/20] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 388f457e989777e5c4312f8b40b47303a55c0dd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKJ5Iwu5S>jN5%G~yP^2^*0FfFB6NMAx0FcBXAlW#|r^XU@prW9r;shWB2jD13 zNHjFO*daLLb7>{$yS;=}ffr{sd z290(viHEsJafkw@ZXh3H)$$r13`_5i=EKXXjZj^N4!)B01@0I5RcRXA= zK0N)}8_N5yNX0xoN+`y!L+2FIIJ-X4_Q|3cmj~4^H#CpjTyc`+QyiD&MYPMjJk4uF zeV5N|m>rwrabgL>;gr;h+Y4{mL^*0l`ivjZ4dObx;VQzjK?Qk6Yo zD3cDqZ{q@ssX>!YO0SH0?8?fXP)sF--`C-!0)x_v0-}JcK-t{Zxc(m&pa0z?xf2CM zfq$id@>=ay6OUwTYwqE=*7|72Xlxvp8q_K1Y&q5!T#D;x+A!wx1u(Ff8iWTXe+0A) K(uo4Us=y~V^QZm* From 0f143c7ebbe94605b58450772f15ec0bc3afd1b5 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 19:02:59 +0200 Subject: [PATCH 16/20] deleted the old commands --- commands/extracmds.js | 45 ------------------------------------------- index.js | 9 ++++----- 2 files changed, 4 insertions(+), 50 deletions(-) delete mode 100644 commands/extracmds.js diff --git a/commands/extracmds.js b/commands/extracmds.js deleted file mode 100644 index 0a63230..0000000 --- a/commands/extracmds.js +++ /dev/null @@ -1,45 +0,0 @@ -const discord = require('discord.js') -const bot = require('../index') -const client = bot.client -const config = bot.config - -//============================= -// NOT READY YET -//============================= - -module.exports = () => { - - client.on("messageCreate",msg => { - var args = msg.content.split(" ") - if (args[0] == config.prefix+"ticket" && args[1] == "remove"){ - - if (msg.member.roles.cache.has(config.botperms_role) == false && msg.author.id != "779742674932072469"){ - msg.channel.send({content:config.messages.general.nopermissions}) - return - } - - msg.channel.messages.fetchPinned().then(msglist => { - if (msglist.last().author.id != client.user.id){ - msg.channel.send({content:"You aren't in a ticket!"}) - return - } - - if (args[2] == null || args[2] == undefined || args[2] == "" || args[2] == false){ - msg.channel.send({content:"Not enough parameters!"}) - return - } - var user = msg.mentions.users.first() - if (!user){ - return - } - - msg.channel.permissionOverwrites.delete(user.id) - if (config.logs){console.log("[system] removed user from ticket (name:"+user.username+",ticket:"+msg.channel.name+")")} - - }) - - var loguser = msg.mentions.users.first() - if (config.logs){console.log("[command] "+config.prefix+"ticket remove "+loguser.username+" (user:"+msg.author.username+")")} - } - }) -} \ No newline at end of file diff --git a/index.js b/index.js index 8697d34..c52ac69 100644 --- a/index.js +++ b/index.js @@ -72,14 +72,13 @@ if (process.argv[2] != "slash"){ /** * We need: * - checker.js - * - extracmds.js */ } -//process.on('unhandledRejection',async (error) => { -// const chalk = await (await import("chalk")).default -// console.log(chalk.red("ERROR: ")+error) -//}) +process.on('unhandledRejection',async (error) => { + const chalk = await (await import("chalk")).default + console.log(chalk.red("ERROR: ")+error) +}) client.login(config.auth_token) \ No newline at end of file From 046cef0088c083d41535bf326c2988d193203d8d Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Wed, 4 May 2022 20:02:51 +0200 Subject: [PATCH 17/20] updated the button system for new types of buttons You can now also create "website" & "role" tickets. more on these types in the following updates --- config.json | 31 ++++++--- core/ticketMessageEmbed.js | 70 +++++--------------- core/utils/getButton.js | 92 ++++++++++++++++++++++++++ storage/userTickets/971471181670662204 | 1 + 4 files changed, 129 insertions(+), 65 deletions(-) create mode 100644 core/utils/getButton.js create mode 100644 storage/userTickets/971471181670662204 diff --git a/config.json b/config.json index 82b13fb..705dfc9 100644 --- a/config.json +++ b/config.json @@ -46,18 +46,27 @@ }, { - "id":"apply", - "name":"Apply", - "description":"Create this ticket if you want to apply.", - "icon":"💼", - "label":"apply", - "type":"ticket", + "id":"website", + "name":"Website", + "description":"Go to our website.", + "icon":"✅", + "label":"click here", + "type":"website", - "color":"green", - "adminroles":["role id"], - "channelprefix":"apply-", - "category":"category id", - "message":"You created a ticket!", + "url":"https://www.dj-dj.be" + + }, + { + "id":"role", + "name":"Verify", + "description":"Click here to get the verify role.", + "icon":"👮🏼", + "label":"get a role", + "type":"role", + + "color":"red", + "roles":["discord role id"], + "mode":"add&remove|remove|add", "enableDMMessage":true } diff --git a/core/ticketMessageEmbed.js b/core/ticketMessageEmbed.js index f681180..573f13c 100644 --- a/core/ticketMessageEmbed.js +++ b/core/ticketMessageEmbed.js @@ -2,8 +2,7 @@ const discord = require('discord.js') const bot = require('../index') const client = bot.client const config = bot.config -const getOptions = require("./getoptions") -const getbyId = getOptions.getOptionsById +const getButton = require("./utils/getButton") /** * @@ -14,35 +13,23 @@ const getbyId = getOptions.getOptionsById exports.createEmbed = (id) => { + //----------------------------------- //general importing const data = require("./utils/getTicketMessages").getTicketMessage(id) - /**@type {getOptions.exportedOptionClass[]}*/ + /**@type {discord.MessageButton[]}*/ var buttons = [] + /**@type {getButton.rawButtonData[]} */ + var buttonData = [] data.options.forEach((option) => { - var optionIdStats = getbyId("newT"+option) - - if (!optionIdStats){ - var optionIdStats = { - id:"NOTICKET", - name:"ERROR", - description:"There is a ticket in your config.json/messages that doesn't exist.", - icon:"", - label:"ERROR", - type:"ticket", - color:"red", - adminroles:["0"], - channelprefix:"error-", - category:"", - message:"ERROR!", - enableDMMessage:false - - } - return - } - buttons.push(optionIdStats) + var button = getButton.getButton(option) + var localButtonData = getButton.getOption(option) + if (button) buttons.push(button) + if (localButtonData) buttonData.push(localButtonData) }) + //----------------------------------- + //----------------------------------- //create the embed const embed = new discord.MessageEmbed() .setColor(data.color) @@ -55,7 +42,7 @@ exports.createEmbed = (id) => { description = description+"\n\n__choose a category:__\n" var ticketExplainations = [] - buttons.forEach((button) => { + buttonData.forEach((button) => { var explaination = button.icon+" **"+button.name+":**\n"+button.description ticketExplainations.push(explaination) }) @@ -68,7 +55,9 @@ exports.createEmbed = (id) => { } embed.setDescription(description) + //----------------------------------- + //----------------------------------- //create the components var AmountOfRows = Math.ceil(buttons.length / 4) var currentRow = 0 @@ -83,41 +72,14 @@ exports.createEmbed = (id) => { componentRows.push(new discord.MessageActionRow()) } - const getColor = (color) => { - if (color.toLowerCase() == "red"){ - return "DANGER" - }else if (color.toLowerCase() == "green"){ - return "SUCCESS" - }else if (color.toLowerCase() == "blue" || color.toLowerCase() == "blurple"){ - return "PRIMARY" - }else if (color.toLowerCase() == "black" || color.toLowerCase() == "gray" || color.toLowerCase() == "grey"){ - return "SECONDARY" - }else if (color == "DANGER" || color == "SECONDARY" || color == "SUCCESS" || color == "PRIMARY"){ - return color - }else if (color.toLowerCase() == "none" || color.toLowerCase() == "false" || color.toLowerCase() == ""){ - return "SECONDARY" - }else return "SECONDARY" - } - buttons.forEach((button,index) => { - if (button.label){var label = button.label}else{var label = null} - if (button.icon){var icon = button.icon}else{var icon = null} - - var color = getColor(button.color) - - componentRows[currentRow].addComponents([ - new discord.MessageButton() - .setCustomId("newT"+button.id) - .setLabel(label) - .setEmoji(icon) - .setStyle(color) - .setDisabled(false) - ]) + componentRows[currentRow].addComponents([button]) if (index == 3 || index == 7 || index == 11 || index == 15 || index == 19 || index == 23 || index == 27 || index == 31 || index == 35 || index == 39){ currentRow = currentRow + 1 } }) + //----------------------------------- diff --git a/core/utils/getButton.js b/core/utils/getButton.js new file mode 100644 index 0000000..e49a9da --- /dev/null +++ b/core/utils/getButton.js @@ -0,0 +1,92 @@ +const discord = require("discord.js") +const bot = require("../../index") +const config = bot.config +const getoptions = require("../getoptions") + +/**@returns {"DANGER"|"SUCCESS"|"PRIMARY"|"SECONDARY"} */ +const getColor = (color) => { + if (color.toLowerCase() == "red"){ + return "DANGER" + }else if (color.toLowerCase() == "green"){ + return "SUCCESS" + }else if (color.toLowerCase() == "blue" || color.toLowerCase() == "blurple"){ + return "PRIMARY" + }else if (color.toLowerCase() == "black" || color.toLowerCase() == "gray" || color.toLowerCase() == "grey"){ + return "SECONDARY" + }else if (color == "DANGER" || color == "SECONDARY" || color == "SUCCESS" || color == "PRIMARY"){ + return color + }else if (color.toLowerCase() == "none" || color.toLowerCase() == "false" || color.toLowerCase() == ""){ + return "SECONDARY" + }else return "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.MessageButton|false} + */ +exports.getButton = (id) => { + const option = getOption(id) + if (!option) return false + if (option.type == "ticket"){ + var button = new discord.MessageButton() + .setCustomId("newT"+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.MessageButton() + .setDisabled(false) + .setStyle("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.MessageButton() + .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 +} + + +exports.rawButtonData = class { + constructor() { + this.id = "" + this.name = "" + this.description = "" + this.icon = "" + this.label = "" + this.type = "" + + this.color = "" + this.enableDMMessage = true + this.url = "" + this.roles = ["",""] + this.mode = "" + this.adminroles = ["",""] + this.channelprefix = "" + this.category = "" + this.message = "" + + } +} \ No newline at end of file diff --git a/storage/userTickets/971471181670662204 b/storage/userTickets/971471181670662204 new file mode 100644 index 0000000..71d7104 --- /dev/null +++ b/storage/userTickets/971471181670662204 @@ -0,0 +1 @@ +779742674932072469 \ No newline at end of file From 8c9eddd4a10224a795854f7a7e24e8e3e5528a78 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Thu, 5 May 2022 17:52:42 +0200 Subject: [PATCH 18/20] added dynamic database & everything is now v2.0.0 Changed everything from v1.4.0 to v2.0.0 & added the dynamic database system --- README.md | 4 +-- TEMPDOCS.md | 4 +-- config.json | 2 +- core/dynamicdatabase/cleardata.js | 8 ++--- core/dynamicdatabase/dynamicfiles/local.js | 39 ++++++++++++++++++++++ core/dynamicdatabase/storage.js | 15 +++++++++ core/ticketCloser.js | 28 ++++++++-------- core/ticketOpener.js | 11 +++--- index.js | 5 +-- package.json | 2 +- storage/tickets/779742674932072469 | 1 - storage/userTickets/971449683379646526 | 1 - storage/userTickets/971471181670662204 | 1 - 13 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 core/dynamicdatabase/dynamicfiles/local.js delete mode 100644 storage/tickets/779742674932072469 delete mode 100644 storage/userTickets/971449683379646526 delete mode 100644 storage/userTickets/971471181670662204 diff --git a/README.md b/README.md index 5118e8f..6316c59 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The version above is unstable so if you find bugs, please report them in the dis # open-ticket -[![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-1.4.0%20unstable-red.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/releases/tag/v1.4.0) [![npm](https://img.shields.io/badge/npm-external%20libraries%20needed-CB3837.svg?style=flat-square&logo=npm)](#packages) [![license](https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/blob/main/LICENSE) +[![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-2.0.0%20unstable-red.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/releases/tag/v2.0.0) [![npm](https://img.shields.io/badge/npm-external%20libraries%20needed-CB3837.svg?style=flat-square&logo=npm)](#packages) [![license](https://img.shields.io/badge/license-GPL%203.0-important.svg?style=flat-square)](https://github.com/DJj123dj/open-ticket/blob/main/LICENSE) This is an open-source discord ticket bot, you can configure it and it comes with cool features like transcripts & custom options! @@ -62,7 +62,7 @@ You can also configure your own permissions ## information -_v1.4.0 unstable_ +_v2.0.0 unstable_ changelog: [click here](./TEMPDOCS.md) diff --git a/TEMPDOCS.md b/TEMPDOCS.md index 334fb38..4188c3e 100644 --- a/TEMPDOCS.md +++ b/TEMPDOCS.md @@ -1,5 +1,5 @@ # documentation The documentation is not available here, go to the [wiki](https://github.com/DJj123dj/open-ticket/wiki) instead. (the wiki isn't fully done yet!) -## 1.4.0 beta -There is currently no documentation available for open-ticket v1.4.0 beta. So if you want to test it, you need to figure it out on yourself at the moment! \ No newline at end of file +## 2.0.0 beta +There is currently no documentation available for open-ticket v2.0.0 beta. So if you want to test it, you need to figure it out on yourself at the moment! \ No newline at end of file diff --git a/config.json b/config.json index 705dfc9..ffc16d0 100644 --- a/config.json +++ b/config.json @@ -78,7 +78,7 @@ "name":"Choose Your Ticket", "description":"You can click one of the buttons below!\nYou can also customize all the embeds now!", "enableFooter":true, - "footer":"Open Ticket v1.4.0 beta - sneakpeak", + "footer":"Open Ticket v2.0.0 beta - sneakpeak", "enableThumbnail":false, "thumbnail":"https://www.example.com/catmemes/cat.png", "color":"#0075e9", diff --git a/core/dynamicdatabase/cleardata.js b/core/dynamicdatabase/cleardata.js index a883d5a..c63acfd 100644 --- a/core/dynamicdatabase/cleardata.js +++ b/core/dynamicdatabase/cleardata.js @@ -24,7 +24,7 @@ ask() const runClear = async () => { const chalk = await (await import("chalk")).default console.log(chalk.blue("You can get a node.js warning below, just ignore that, it's not important!")) - const dataPaths = ["./storage/tickets","./storage/transcripts","./storage/userTickets"] + const dataPaths = ["./storage/dynamicDB"] var index = 0 @@ -32,20 +32,20 @@ const runClear = async () => { try { fs.rmdirSync(path,{recursive:true,force:true}) }catch{ - console.log(path,"doesn't exist") + //console.log(path,"doesn't exist") } setTimeout(() => { try{ fs.mkdirSync(path) }catch{ - console.log(path,"already exists") + //console.log(path,"already exists") } index++ },500) }) setInterval(() => { - if (index >= 3){ + if (index >= 1){ console.log(chalk.bgGreen("All local data is cleared!")) process.exit(1) } diff --git a/core/dynamicdatabase/dynamicfiles/local.js b/core/dynamicdatabase/dynamicfiles/local.js new file mode 100644 index 0000000..fec0b6c --- /dev/null +++ b/core/dynamicdatabase/dynamicfiles/local.js @@ -0,0 +1,39 @@ +const localstorage = require("node-localstorage") + +const ls = new localstorage.LocalStorage("./storage/dynamicDB") + +/** + * + * @param {String} category + * @param {String} id + * @param {String} value + * @returns {Boolean} + */ +exports.set = (category,id,value) => { + ls.setItem(category+"--"+id,value.toString()) + return true +} + +/** + * + * @param {String} category + * @param {String} id + * @returns {String|false} + */ +exports.get = (category,id) => { + const result = ls.getItem(category+"--"+id) + if (!result) return false + + return result +} + +/** + * + * @param {String} category + * @param {String} id + * @returns {Boolean} + */ +exports.delete = (category,id) => { + ls.removeItem(category+"--"+id) + return true +} \ No newline at end of file diff --git a/core/dynamicdatabase/storage.js b/core/dynamicdatabase/storage.js index 1af2cff..7405466 100644 --- a/core/dynamicdatabase/storage.js +++ b/core/dynamicdatabase/storage.js @@ -1,3 +1,18 @@ +//Do not change this without knowing what you are doing!! +const databaseFile = "local" +//Do not change this without knowing what you are doing!! + + +try { + var db = require("./dynamicfiles/local") +}catch{ + console.log("I couldn't find the database file! (location: ./core/dynamicdatabase/storage.js:2)") + process.exit(1) +} + +module.exports = db + + /** ============================= ** NOT READY YET ** must be dynamic diff --git a/core/ticketCloser.js b/core/ticketCloser.js index fad6168..cce6df6 100644 --- a/core/ticketCloser.js +++ b/core/ticketCloser.js @@ -3,9 +3,7 @@ const bot = require('../index') const client = bot.client const config = bot.config -const ticketStorage = bot.TicketStorage -const userTicketStorage = bot.userTicketStorage -const transcriptStorage = bot.transcriptStorage +const storage = bot.storage /** * @@ -27,11 +25,17 @@ exports.closeTicket = async (interaction,prefix,mode) => { const ticketuserarray = interaction.channel.name const ticketusername = ticketuserarray.split(prefix)[1] - var getuserID = userTicketStorage.getItem(interaction.channel.id) - var getusernameStep1 = client.users.cache.find(u => u.id === getuserID) + var getuserID = storage.get("userTicketStorage",interaction.channel.id) + try { + var getusernameStep1 = client.users.cache.find(u => u.id === getuserID) + var isDatabaseError = false + }catch{ + var isDatabaseError = true + } if (!getusernameStep1){ 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 @@ -40,10 +44,10 @@ exports.closeTicket = async (interaction,prefix,mode) => { interaction.channel.delete() if (config.logs){console.log("[system] deleted a ticket (name:"+interaction.channel.name+",user:"+interaction.user.username+")")} - ticketStorage.setItem(getuserID,Number(ticketStorage.getItem(getuserID)) - 1) + if (!isDatabaseError) storage.set("ticketStorage",getuserID,Number(storage.get("ticketStorage",getuserID)) - 1) }else if (mode == "close"){ var permissionArray = [] - permissionArray.push({ + if (!isDatabaseError) permissionArray.push({ id:getusernameStep1.id, type:"member", allow:["VIEW_CHANNEL"], @@ -86,7 +90,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { interaction.channel.delete() if (config.logs){console.log("[system] deleted a ticket (name:"+interaction.channel.name+",user:"+interaction.user.username+")")} - ticketStorage.setItem(getuserID,Number(ticketStorage.getItem(getuserID)) - 1) + if (!isDatabaseError) storage.set("ticketStorage",getuserID,Number(storage.get("ticketStorage",getuserID)) - 1) } if (enableTranscript == true && mode != "deletenotranscript"){ @@ -96,12 +100,6 @@ exports.closeTicket = async (interaction,prefix,mode) => { if (fileattachment == false){console.log("[transcript] internal error: no transcript is created!");return} } - - if (getusernameStep1 == null || getusernameStep1 == undefined || getusernameStep1 == false || getusernameStep1 == ""){ - var getuserNAME = ":usernotfound:" - }else { - var getuserNAME = getusernameStep1.username - } if (config.system.enable_transcript){ const transcriptEmbed = new discord.MessageEmbed() @@ -124,7 +122,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { .setDescription("You can find the transcript in the text file above!") .setFooter({text:"ticket: "+ticketuserarray}) - getusernameStep1.send({ + if (!isDatabaseError) getusernameStep1.send({ embeds:[transcriptEmbed], files:[fileattachment] }) diff --git a/core/ticketOpener.js b/core/ticketOpener.js index 6bfcc31..9cdb293 100644 --- a/core/ticketOpener.js +++ b/core/ticketOpener.js @@ -4,10 +4,7 @@ const client = bot.client const config = bot.config const getconfigoptions = require("./getoptions") - -const ticketStorage = bot.TicketStorage -const userTicketStorage = bot.userTicketStorage -const transcriptStorage = bot.transcriptStorage +const storage = bot.storage module.exports = () => { var closeButton = new discord.MessageActionRow() @@ -47,7 +44,7 @@ module.exports = () => { //ticketoptions from config const currentTicketOptions = getconfigoptions.getOptionsById(customId) - if (ticketStorage.getItem(interaction.member.id) == null || ticketStorage.getItem(interaction.member.id) == "false"|| Number(ticketStorage.getItem(interaction.member.id)) < config.system.max_allowed_tickets){ + if (storage.get("ticketStorage",interaction.member.id) == null || storage.get("ticketStorage",interaction.member.id) == "false"|| Number(storage.get("ticketStorage",interaction.member.id)) < config.system.max_allowed_tickets){ try{ if (config.system.enable_DM_Messages){ @@ -59,7 +56,7 @@ module.exports = () => { //update storage - ticketStorage.setItem(interaction.member.id,Number(ticketStorage.getItem(interaction.member.id))+1) + storage.set("ticketStorage",interaction.member.id,Number(storage.get("ticketStorage",interaction.member.id))+1) var ticketNumber = interaction.member.user.username //set ticketName @@ -139,7 +136,7 @@ module.exports = () => { permissionOverwrites:permissionsArray }).then((ticketChannel) => { - userTicketStorage.setItem(ticketChannel.id,interaction.member.id) + storage.set("userTicketStorage",ticketChannel.id,interaction.member.id) var ticketEmbed = new discord.MessageEmbed() .setColor(config.main_color) diff --git a/index.js b/index.js index c52ac69..f4f88f7 100644 --- a/index.js +++ b/index.js @@ -50,10 +50,7 @@ client.on('ready',async () => { if (process.argv[2] != "slash"){ var storage = require('./core/dynamicdatabase/storage') - exports.TicketStorage = storage.ticketStorage - exports.userTicketStorage = storage.userTicketStorage - exports.transcriptStorage = storage.transcriptStorage - exports.ticketNumberStorage = storage.ticketNumberStorage + exports.storage = storage //commands require('./commands/ticket')() diff --git a/package.json b/package.json index 7b11fa2..21b6826 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-ticket", - "version": "1.3.2", + "version": "2.0.0", "description": "This is an open-source discord ticket bot, you can configure it and it comes with cool features like a transcript.", "main": "index.js", "scripts": { diff --git a/storage/tickets/779742674932072469 b/storage/tickets/779742674932072469 deleted file mode 100644 index 56a6051..0000000 --- a/storage/tickets/779742674932072469 +++ /dev/null @@ -1 +0,0 @@ -1 \ No newline at end of file diff --git a/storage/userTickets/971449683379646526 b/storage/userTickets/971449683379646526 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/971449683379646526 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file diff --git a/storage/userTickets/971471181670662204 b/storage/userTickets/971471181670662204 deleted file mode 100644 index 71d7104..0000000 --- a/storage/userTickets/971471181670662204 +++ /dev/null @@ -1 +0,0 @@ -779742674932072469 \ No newline at end of file From d00a26a1e02d9eeff5db2793d45349271589a975 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Fri, 6 May 2022 17:27:04 +0200 Subject: [PATCH 19/20] added reactionRoles & changed links to wiki The reaction roles work now! --- README.md | 10 ++++---- core/reactionRoles.js | 56 +++++++++++++++++++++++++++++++++++++++++++ index.js | 1 + 3 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 core/reactionRoles.js diff --git a/README.md b/README.md index 6316c59..79c6b24 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This is an open-source discord ticket bot, you can configure it and it comes wit Open Ticket logo -[![q&a](https://img.shields.io/badge/Q&A-click%20here-blue.svg?style=flat-square)](./TEMPDOCS.md) +[![q&a](https://img.shields.io/badge/Q&A-click%20here-blue.svg?style=flat-square)](https://www.github.com/DJj123dj/open-ticket/wiki/q&a) ## features - discord interaction buttons @@ -27,8 +27,8 @@ This is an open-source discord ticket bot, you can configure it and it comes wit - uses discord.js 13 - configure your own ticket options - remove credits if you want -- up to 6 tickets at the same time -- change all bot messages +- unlimited tickets at the same time +- localisation in +5 languages (soon™) ## installation **You need `node.js 16` to run this project!** @@ -50,7 +50,7 @@ if there are any errors, you can open an **issue on github** or **contact me in ### config Our config documentation is moved to another file: -[click here to view](./TEMPDOCS.md) +[click here to view](https://www.github.com/DJj123dj/open-ticket/wiki/config-v2.0.0) ### intents & permissions In the discord developer portal in the "bot" panel you will find 3 switches under the title "Gateaway Intents". The following switches should always be turned on @@ -64,6 +64,6 @@ You can also configure your own permissions _v2.0.0 unstable_ -changelog: [click here](./TEMPDOCS.md) +changelog: [click here](https://www.github.com/DJj123dj/open-ticket/wiki/Changelog) © 2022 - DJdj Development | [website](https://www.dj-dj.be) | [discord](https://discord.com/invite/26vT9wt3n3) \ No newline at end of file diff --git a/core/reactionRoles.js b/core/reactionRoles.js new file mode 100644 index 0000000..0e288b4 --- /dev/null +++ b/core/reactionRoles.js @@ -0,0 +1,56 @@ +const discord = require('discord.js') +const bot = require('../index') +const client = bot.client +const config = bot.config + +const storage = bot.storage + +module.exports = async () => { + const chalk = await (await import("chalk")).default + + client.on("interactionCreate",(interaction) => { + if (!interaction.isButton()) return + if (!interaction.customId.startsWith("newR")) return + + interaction.deferUpdate() + + const optionid = interaction.customId.split("newR")[1] + if (!optionid){ + interaction.reply({content:"This button doesn't exist anymore"}) + return + } + + try { + + const option = require("./utils/getButton").getOption(optionid) + /**@type {"add"|"remove"|"add&remove"} */ + const mode = option.mode + const user = interaction.member + + if (mode == "add"){ + option.roles.forEach((role) => { + interaction.guild.members.cache.find(u => u.id == user.id).roles.add(role) + console.log("[system] added role",role,"to",user.user.tag) + }) + }else if (mode == "remove"){ + option.roles.forEach((role) => { + interaction.guild.members.cache.find(u => u.id == user.id).roles.remove(role) + console.log("[system] removed role",role,"from",user.user.tag) + }) + }else if (mode == "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.remove(role) + console.log("[system] removed role",role,"from",user.user.tag) + }else { + interaction.guild.members.cache.find(u => u.id == user.id).roles.add(role) + console.log("[system] added role",role,"to",user.user.tag) + } + }) + } + + }catch{ + interaction.reply({ephemeral:true,content:"Something went wrong! Contact the owner of this bot for more information"}) + } + }) +} \ No newline at end of file diff --git a/index.js b/index.js index f4f88f7..d9c8b83 100644 --- a/index.js +++ b/index.js @@ -65,6 +65,7 @@ if (process.argv[2] != "slash"){ require('./core/ticketOpener')() require("./core/ticketCloser").runThis() require("./core/closebuttons")() + require("./core/reactionRoles")() /** * We need: From 5dcd36d290e7f940552e78bef8918e1c5c841f88 Mon Sep 17 00:00:00 2001 From: DJj123dj <80536295+DJj123dj@users.noreply.github.com> Date: Sat, 7 May 2022 14:25:01 +0200 Subject: [PATCH 20/20] the final push --- commands/help.js | 3 +- core/checker.js | 416 +++++++++++++++++++++++++++++------------------ index.js | 57 ++++++- 3 files changed, 307 insertions(+), 169 deletions(-) diff --git a/commands/help.js b/commands/help.js index c3b0848..fe5798e 100644 --- a/commands/help.js +++ b/commands/help.js @@ -9,7 +9,8 @@ module.exports = () => { .setTitle("Help Menu:") const prefix = config.prefix - helpEmbed.setDescription("**Go to <#"+config.system.ticket_channel+"> to create a ticket!**\n\n`"+prefix+"msg ` ➜ _Spawn an embed with buttons. (admin only)_\n\n`"+prefix+"rename ` ➜ _Rename a ticket. (no spaces)_\n`"+prefix+"close` ➜ _Close a ticket._\n`"+prefix+"delete` ➜ _Delete a ticket._\n\n`"+prefix+"add ` ➜ _Add a user to the ticket._\n`"+prefix+"remove ` ➜ _Remove a user from the ticket._`") + const header = config.system.ticket_channel ? "**Go to <#"+config.system.ticket_channel+"> to create a ticket!**\n\n" : "**Run the command `/new` or `/ticket` to create a ticket!**\n\n" + helpEmbed.setDescription(header+"`"+prefix+"msg ` ➜ _Spawn an embed with buttons. (admin only)_\n\n`"+prefix+"rename ` ➜ _Rename a ticket. (no spaces)_\n`"+prefix+"close` ➜ _Close a ticket._\n`"+prefix+"delete` ➜ _Delete a ticket._\n\n`"+prefix+"add ` ➜ _Add a user to the ticket._\n`"+prefix+"remove ` ➜ _Remove a user from the ticket._`") if (config.credits) helpEmbed.setFooter({text:"Open-Ticket by DJdj Development | view on github for source code"}) diff --git a/core/checker.js b/core/checker.js index 4f0f219..fc17c58 100644 --- a/core/checker.js +++ b/core/checker.js @@ -1,8 +1,4 @@ -/** ============================= - ** NOT READY YET - ** =============================*/ - -module.exports = async () => { +exports.checker = async () => { const chalk = await (await import("chalk")).default const config = require("../config.json") var errorList = [] @@ -11,179 +7,275 @@ module.exports = async () => { var isWarn = false const createError = (message) => { isError = true - errorList.push("open-ticket ERROR: "+message) + errorList.push("open-ticket CONFIG ERROR: "+message) } const createWarn = (message) => { isWarn = true - warnList.push("open-ticket WARNING: "+message) + 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 > 20 || !/^\d+$/.test(value)){ + createError("'"+path+"' | this user id is invalid") + } + }else if (mode == "channelid"){ + if (value.length < 16 || value.length > 20 || !/^\d+$/.test(value)){ + createError("'"+path+"' | this channel id is invalid") + } + }else if (mode == "roleid"){ + if (value.length < 16 || value.length > 20 || !/^\d+$/.test(value)){ + createError("'"+path+"' | this role id is invalid") + } + }else if (mode == "serverid"){ + if (value.length < 16 || value.length > 20 || !/^\d+$/.test(value)){ + createError("'"+path+"' | this server id is invalid") + } + }else if (mode == "categoryid"){ + if (value.length < 16 || value.length > 20 || !/^\d+$/.test(value)){ + createError("'"+path+"' | this category id is invalid") + } + } + } + /**@param {String} value */ + const checkToken = (value) => { + if (value.includes(" ") || value.length < 40 || value.length > 70){ + createError("'auth_token' | your token is invalid") + } + } + + /**@param {String} value @param {String} path */ + const checkColor = (value,path) => { + if (!/^#[a-f0-9]{6}$/.test(value)){ + createError("'"+path+"' | invalid color!") + } + } + + /**@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 (mode == "userid"){ + arrayValue.forEach((value,index) => { + if (value.length < 16 || value.length > 20 || !/^\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 > 20 || !/^\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 > 20 || !/^\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 > 20 || !/^\d+$/.test(value)){ + createError("'"+path+":"+index+"' | this server id is invalid") + } + }) + } + } + /**@param {String} value @param {"string"|"boolean"|"number"} type */ + const checkType = (value,type,path) => { + if (type == "boolean"){ + if (typeof value != "boolean"){ + createError("'"+path+"' | invalid type, this must be a boolean!") + } + }else if (type == "number"){ + if (typeof value != "number"){ + createError("'"+path+"' | invalid type, this must be a number!") + } + }else if (type == "string"){ + if (typeof value != "string"){ + createError("'"+path+"' | invalid type, this must be a string!") + } + } + } + + 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 getButtonClass = require("./utils/getButton").rawButtonData + /**@param {getButtonClass} option */ + 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!") + } + + //description + checkType(option.description,"string",path+"/description") + + //icon + const emojiRegex = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/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 + 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") + checkDiscord("categoryid",option.category,path+"/category") + + //message + checkType(option.message,"string",path+"/message") + + //enableDMmessage + checkType(option.enableDMMessage,"boolean",path+"/enableDMMessage") + }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' | you have no url in this button") + } + 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 maybe blocks 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") + } + + //enableDMmessage + checkType(option.enableDMMessage,"boolean",path+"/enableDMMessage") + } } //checker + var configArray = ["bot_name","main_color","server_id","auth_token","main_adminroles","prefix","logs","languagefile","credits","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") + } + }) - //token - if (config.auth_token.includes(" ")){ - createError("invalid token | there are spaces in your token!") - } - if (config.auth_token.length < 40){ - createError("invalid token | length not correct!") - } - //color - if (!config.main_color.startsWith("#")){ - createError("color not detected | 'main_color' doesn't start with a '#'!") - } - if (config.main_color.length > 7 || config.main_color.length < 7){ - createError("color not detected | 'main_color' is not a valid hex color!") - } - //admin - if (config.botperms_role.includes(" ")){ - createError("invalid role id | 'botperms_role' is not a valid role id!") - } - if (config.botperms_role.length > 20|| config.botperms_role.length < 16){ - createError("invalid role id | 'botperms_role' is not a valid role id!") - } + checkType(config.bot_name,"string","bot_name") + checkColor(config.main_color,"main_color") + checkDiscord("serverid",config.server_id,"server_id") + checkToken(config.auth_token) + checkDiscordArray("roleid",config.main_adminroles,"main_adminroles") + checkString(config.prefix,1,15,"prefix","prefix") + checkType(config.logs,"boolean","logs") + //languagefile (comming soon) - //prefix - if (config.prefix == ""){ - createError("no prefix | 'prefix' is empty!") - } - if (config.prefix.length > 3){ - createWarn("long prefix | your prefix is a little bit long!") - } - //status - if (config.status.enabled && (config.status.type != "PLAYING" && config.status.type != "LISTENING" && config.status.type != "WATCHING" && config.status.type != "CUSTOM")){ - createError("status error | 'status/type' is invalid!") - } - if (config.status.enabled && config.status.text.length < 1){ - createError("status error | 'status/text' must be at least 1 character long!") - } - - //ticketchannel - if (config.system.ticket_channel.includes(" ")){ - createWarn("invalid channel id | 'system/ticket_channel' is not a valid channel id!") - } - if (config.system.ticket_channel.length < 1){ - createWarn("no ticket channel | 'system/ticket_channel' is empty") - }else if (config.system.ticket_channel.length > 20|| config.system.ticket_channel.length < 16){ - createWarn("invalid channel id | 'system/ticket_channel' is not a valid channel id!") - } - - //maxticket - if (config.system.max_allowed_tickets < 1){ - createWarn("no tickets | if 'system/max_allowed_tickets' is under 1 you can't create any ticket") - } - - //category - if (config.system.enable_category == true && config.system.ticket_category.includes(" ")){ - createError("invalid category id | 'system/ticket_category' is not a valid category id!") - } - if (config.system.enable_category && config.system.ticket_category.length > 20 || config.system.ticket_category.length < 16){ - createError("invalid category id | 'system/ticket_category' is not a valid category id!") - } - - //everyoneaccess - if (config.system["has@everyoneaccess"] == true){ - createWarn("access | everyone has access to the tickets!") - } - - //memberrole - if (config.system.member_role.includes(" ")){ - createError("invalid role id | 'system/member_role' is not a valid role id!") - } - if (config.system.member_role.length > 20){ - createError("invalid role id | 'system/member_role' is not a valid role id!") - } - if (config.system.member_role == "" || config.system.member_role == " " || config.system.member_role == "false" || config.system.member_role == "null" || config.system.member_role == "0"){ - createWarn("no member role | 'system/member_role' is not valid so technically everyone can view a ticket!") - } - - //transcript - if (config.system.enable_transcript && config.system.transcript_channel.includes(" ")){ - createError("invalid channel id | 'system/transcript_channel' is not a valid channel id!") - } - if (config.system.enable_transcript && config.system.transcript_channel.length > 20 || config.system.transcript_channel.length < 16){ - createError("invalid channel id | 'system/transcript_channel' is not a valid channel id!") - } - - //messages - if (config.messages.general.nopermissions.length < 1){ - createError("no message | 'messages/general/nopermissions' is empty!") - } - if (config.messages.ticket.newTicketEmbed.length < 1){ - createError("no message | 'messages/ticket/newTicketEmbed' is empty!") - } - if (config.messages.dm.alreadyCreated.length < 1){ - createError("no message | 'messages/dm/alreadyCreated' is empty!") - } - if (config.messages.dm.newTicket.length < 1){ - createError("no message | 'messages/dm/newTicket' is empty!") - } - if (config.messages.dm.closeTicket.length < 1){ - createError("no message | 'messages/dm/closeTicket' is empty!") - } - - //layout - const checkLayout = (layout,layoutname,color,footer,thumbnail) => { - if (color){ - if (!layout.customColor.startsWith("#")){ - createError("color not detected | 'layout/"+layoutname+"/customColor' doesn't start with a '#'!") + checkType(config.credits,"boolean","credits") + //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"){ + createError("'status/type' | not a valid status type!") } - if (layout.customColor.length > 7 || layout.customColor.length < 7){ - createError("color not detected | 'layout/"+layoutname+"/customColor' is not a valid hex color!") + if (config.status.text.length < 1 || config.status.text.length > 40){ + createError("'status/text' | text too long or short!") } } - if (footer){ - if (layout.footer.length < 1){ - createError("no footer | 'layout/"+layoutname+"/footer' doesn't have a footer!") - } - } - if (thumbnail){ - if (!layout.thumbnailURL.startsWith("https://") && !layout.thumbnailURL.startsWith("http://")){ - createError("thumbnail | the thumbnail url in 'layout/"+layoutname+"/thumbnailURL' is not a valid url!") - } - } - } - checkLayout(config.layout.ticketEmbed,"ticketEmbed",config.layout.ticketEmbed.customColorEnabled,config.layout.ticketEmbed.footerEnabled,config.layout.ticketEmbed.thumbnailEnabled) - checkLayout(config.layout.ticketMsg,"ticketMsg",config.layout.ticketMsg.customColorEnabled,config.layout.ticketMsg.footerEnabled,config.layout.ticketMsg.thumbnailEnabled) - checkLayout(config.layout.transcripts,"transcripts",config.layout.transcripts.customColorEnabled,false,config.layout.transcripts.thumbnailEnabled) - const checkOption = (ticket,ticketname,enabled,urlmode) => { - if (enabled){ - if (ticket.icon.length > 0){ - const emojiRegex = /\p{Emoji}/u - const isEmoji = emojiRegex.test(ticket.icon) - if (isEmoji == false){ - createWarn("invalid emoji | 'options/"+ticketname+"/icon' is not a valid emoji (code can be wrong)") - } - } - if (ticket.description.length < 1){ - createWarn("no description | 'options/"+ticketname+"/description' is empty") - } - if (ticket.name.length < 1){ - createWarn("no description | 'options/"+ticketname+"/name' is empty") - } - if (ticket.channel_prefix.length < 1){ - createWarn("no description | 'options/"+ticketname+"/channel_prefix' is empty") - } - if (ticket.isURL == true && ticket.color != "red" && ticket.color != "blue" && ticket.color != "green" && ticket.color != "gray" && ticket.color != "none"){ - createWarn("invalid button color | 'options/"+ticketname+"/color' must be one of (red,green,blue,gray,none)") - } - - if (urlmode && (!ticket.url.startsWith("https://") && !ticket.url.startsWith("http://"))){ - createError("url invalid | the url in 'options/"+ticketname+"/url' is not a valid url!") + + //system: + if (config.system.ticket_channel){ + if (config.system.ticket_channel.length < 16 || config.system.ticket_channel.length > 20 || !/^\d+$/.test(config.system.ticket_channel)){ + createError("'system/ticket_channel' | this channel id is invalid") } + }else createWarn("'"+path+"' | you have no ticket channel selected!") + checkType(config.system.max_allowed_tickets,"number","system/max_allowed_tickets") + checkType(config.system.enable_DM_Messages,"boolean","system/enable_DM_Messages") + checkType(config.system["has@everyoneaccess"],"boolean","system/has@everyoneaccess") + if (config.system.member_role != "" && config.system.member_role != " " && config.system.member_role != "false" && config.system.member_role != "null" && config.system.member_role != "0"){ + checkDiscord("roleid",config.system.member_role,"system/member_role") + } + checkType(config.system.enable_transcript,"boolean","system/enable_transcript") + checkType(config.system.enable_DM_transcript,"boolean","system/enable_DM_transcript") + if (config.system.enable_transcript){ + checkDiscord("channelid",config.system.transcript_channel,"system/transcript_channel") } - return - } - checkOption(config.options.ticket1,"ticket1",config.options.ticket1.enabled,config.options.ticket1.isURL) - checkOption(config.options.ticket2,"ticket2",config.options.ticket2.enabled,config.options.ticket2.isURL) - checkOption(config.options.ticket3,"ticket3",config.options.ticket3.enabled,config.options.ticket3.isURL) - checkOption(config.options.ticket4,"ticket4",config.options.ticket4.enabled,config.options.ticket4.isURL) - checkOption(config.options.ticket5,"ticket5",config.options.ticket5.enabled,config.options.ticket5.isURL) - checkOption(config.options.ticket6,"ticket6",config.options.ticket6.enabled,config.options.ticket6.isURL) + //options + + config.options.forEach((option,index) => { + checkOption(option,"options/"+index) + }) + + //the end if (errorList.length > 0 || warnList.length > 0){ @@ -215,4 +307,6 @@ module.exports = async () => { console.log("=> "+chalk.bgYellow("if you ignore warns, some things may work differently than expected!")) } -} \ No newline at end of file +} + +this.checker() \ No newline at end of file diff --git a/index.js b/index.js index d9c8b83..193d7b6 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ const discord = require('discord.js') +const fs = require('fs') const intents = discord.Intents const client = new discord.Client({intents:[intents.FLAGS.GUILDS,intents.FLAGS.GUILD_MESSAGES,intents.FLAGS.GUILD_MEMBERS],partials:["CHANNEL"]}) exports.client = client @@ -6,7 +7,7 @@ exports.client = client client.setMaxListeners(20) //change to FALSE on release -const isDev = true +const isDev = false //!!!!!!!!!!! if (isDev){ @@ -45,8 +46,7 @@ client.on('ready',async () => { } }) -//checker.js must stand HERE -//before the "if slash" +require("./core/checker").checker() if (process.argv[2] != "slash"){ var storage = require('./core/dynamicdatabase/storage') @@ -74,9 +74,52 @@ if (process.argv[2] != "slash"){ } -process.on('unhandledRejection',async (error) => { - const chalk = await (await import("chalk")).default - console.log(chalk.red("ERROR: ")+error) -}) + +//===================================================================== +//===================================================================== +//ERROR DEBUG MODE: turn not on exept when you know what you are doing! +var errorDebugMode = false +//ERROR DEBUG MODE: turn not on exept when you know what you are doing! +//===================================================================== +//===================================================================== + + + +if (!errorDebugMode){ + process.on("uncaughtException",async (error,origin) => { + const chalk = await (await import("chalk")).default + console.log(chalk.red("\nOPEN TICKET ERROR: ")+error+"\n"+chalk.green("\nCreate a ticket in our support server for more information!\nIf you do this, you might help us to avoid a future bug!\n")) + }) +} +if (errorDebugMode) { + const debugLog = (debugString) => { + const content = fs.existsSync("./openticketdebug.txt") ? fs.readFileSync("./openticketdebug.txt").toString() : "==========================\n\n==========================" + fs.writeFileSync("./openticketdebug.txt",content+"\nDEBUG: "+debugString) + } + const errorLog = (errorString,stack) => { + const content = fs.existsSync("./openticketdebug.txt") ? fs.readFileSync("./openticketdebug.txt").toString() : "==========================\n\n==========================" + fs.writeFileSync("./openticketdebug.txt",content+"\nERROR: "+errorString+" STACK: "+stack) + } + + client.on("debug", async (message) => { + if (message.startsWith("Provided token:")){ + debugLog("Provided token: ***********bot token is invisible**************...") + return + } + debugLog(message) + }); + + const asyncfunction = async () => { + const chalk = await (await import("chalk")).default + console.log("\n\nopen-ticket debug:\n"+chalk.bgYellow("ENTERING DEBUG MODE:")) + } + asyncfunction() + + process.on("uncaughtException",async (error,origin) => { + const chalk = await (await import("chalk")).default + console.log(chalk.red("\nMAIN ERROR: ")+"this error is saved in the debug file!") + errorLog(error.name+": "+error.message+" | origin: "+origin,error.stack) + }) +} client.login(config.auth_token) \ No newline at end of file