diff --git a/README.md b/README.md index ef34e4c..a5be7c1 100644 --- a/README.md +++ b/README.md @@ -18,47 +18,53 @@ This is an open-source discord ticket bot, you can configure it and it comes wit ### Thanks to these people: - DJ-Lite: for doing the administration - Sanke: for translation & new ideas +- Everyone that reported a bug +#### And our translators! - ExothDE: for translation - 1x1: for translation - R0JO: for translation -- Everyone that reported a bug +- t0miiis for translation ## features -- discord buttons +- discord interaction buttons - slash & text commands - close, delete & re-open tickets -- translation (we are searching for translators!) +- translation in 8 different languages - dynamic database system -- advanced transcripts +- transcripts - add/remove users from a ticket -- large embed customisation -- create custom ticket names, icons & colors -- create url's & reaction roles! -- uses discord.js v13 -- configure your own ticket options -- remove credits if you want (don't do it please 😉) -- unlimited tickets at the same time +- custom colors & name for embeds +- customise your ticket +- create url buttons for websites +- create reaction roles +- build with discord.js v14 +- configure everything per ticket +- remove credits if you want :( +- unlimited tickets spread over multiple messages + +*comming soon:* +- dashboard +- view transcripts in dashboard +- share transcripts +- close with reason +- more translation ## installation **You need `node.js 16` to run this project!** -**At this moment you still need discord.js v13!** -This will change soon! - -You can clone or download this project. - ### packages - chalk => version `latest` - discord.js => version `latest` - express => version `latest` - node-localstorage => version `latest` +- axios => version `latest` You can install all of them by running `npm install` ## running you can run `node index.js` but i would recommend you to use `npm start` -if there are any errors, you can open an **issue on github** or **contact me in the discord server**. +if there are any errors, you can open an **issue on github** or **contact me in our discord server**. ### config Our config documentation is moved to another file: @@ -78,4 +84,4 @@ _v2.4.0 stable_ 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 +© 2022 - DJdj Development | [website](https://www.dj-dj.be) | [discord](https://discord.dj-dj.be) | [terms of service](https://www.dj-dj.be/terms) \ No newline at end of file diff --git a/commands/add.js b/commands/add.js index b4232f3..074acd7 100644 --- a/commands/add.js +++ b/commands/add.js @@ -15,6 +15,7 @@ module.exports = () => { if (!msg.guild) return if (!permsChecker.command(msg.author.id,msg.guild.id)){ permsChecker.sendUserNoPerms(msg.author) + return } msg.channel.messages.fetchPinned().then(msglist => { @@ -40,6 +41,7 @@ module.exports = () => { if (!interaction.guild) return if (!permsChecker.command(interaction.user.id,interaction.guild.id)){ permsChecker.sendUserNoPerms(interaction.user) + return } interaction.deferReply() diff --git a/commands/delete.js b/commands/delete.js index 5b9f603..af5a193 100644 --- a/commands/delete.js +++ b/commands/delete.js @@ -13,6 +13,7 @@ module.exports = () => { if (!msg.guild) return if (!permsChecker.command(msg.author.id,msg.guild.id)){ permsChecker.sendUserNoPerms(msg.author) + return } msg.channel.messages.fetchPinned().then(msglist => { @@ -47,6 +48,7 @@ module.exports = () => { if (!interaction.guild) return if (!permsChecker.command(interaction.user.id,interaction.guild.id)){ permsChecker.sendUserNoPerms(interaction.user) + return } interaction.channel.messages.fetchPinned().then(msglist => { diff --git a/commands/remove.js b/commands/remove.js index 8754dab..3511663 100644 --- a/commands/remove.js +++ b/commands/remove.js @@ -15,6 +15,7 @@ module.exports = () => { if (!msg.guild) return if (!permsChecker.command(msg.author.id,msg.guild.id)){ permsChecker.sendUserNoPerms(msg.author) + return } interaction.deferReply() @@ -43,6 +44,7 @@ module.exports = () => { if (!interaction.guild) return if (!permsChecker.command(interaction.user.id,interaction.guild.id)){ permsChecker.sendUserNoPerms(interaction.user) + return } diff --git a/commands/rename.js b/commands/rename.js index 0509ceb..9f716c8 100644 --- a/commands/rename.js +++ b/commands/rename.js @@ -18,6 +18,7 @@ module.exports = () => { if (!msg.guild) return if (!permsChecker.command(msg.author.id,msg.guild.id)){ permsChecker.sendUserNoPerms(msg.author) + return } var newname = msg.content.split(config.prefix+"rename")[1].substring(1) @@ -60,6 +61,7 @@ module.exports = () => { if (!interaction.guild) return if (!permsChecker.command(interaction.user.id,interaction.guild.id)){ permsChecker.sendUserNoPerms(interaction.user) + return } var newname = interaction.options.getString("name") diff --git a/commands/reopen.js b/commands/reopen.js index 3940182..0f89ecd 100644 --- a/commands/reopen.js +++ b/commands/reopen.js @@ -32,18 +32,22 @@ module.exports = () => { }) - client.on("interactionCreate",(interaction) => { + client.on("interactionCreate",async (interaction) => { if (!interaction.isChatInputCommand()) return if (interaction.commandName != "reopen") return - interaction.deferReply() + await interaction.deferReply() - interaction.channel.messages.fetchPinned().then(msglist => { + interaction.channel.messages.fetchPinned().then(async msglist => { var firstmsg = msglist.last() if (firstmsg == undefined || firstmsg.author.id != client.user.id) return msg.channel.send({embeds:[bot.errorLog.notInATicket]}) - interaction.reply({embeds:[bot.errorLog.success(l.commands.reopenTitle,l.commands.reopenDescription)],components:[reopenCommandBar]}) + try { + await interaction.reply({embeds:[bot.errorLog.success(l.commands.reopenTitle,l.commands.reopenDescription)],components:[reopenCommandBar]}) + }catch{ + await interaction.editReply({embeds:[bot.errorLog.success(l.commands.reopenTitle,l.commands.reopenDescription)],components:[reopenCommandBar]}) + } log("command","someone used the 'reopen' command",[{key:"user",value:interaction.user.tag}]) diff --git a/commands/ticket.js b/commands/ticket.js index a381ee3..6735ab5 100644 --- a/commands/ticket.js +++ b/commands/ticket.js @@ -20,6 +20,7 @@ module.exports = () => { if (!msg.guild) return if (!permsChecker.command(msg.author.id,msg.guild.id)){ permsChecker.sendUserNoPerms(msg.author) + return } const id = msg.content.split(config.prefix+"msg")[1].substring(1) ? msg.content.split(config.prefix+"msg")[1].substring(1) : false @@ -43,6 +44,7 @@ module.exports = () => { if (!interaction.guild) return if (!permsChecker.command(interaction.user.id,interaction.guild.id)){ permsChecker.sendUserNoPerms(interaction.user) + return } const id = interaction.options.getString("id") diff --git a/config.json b/config.json index 9dd5158..86ab2ea 100644 --- a/config.json +++ b/config.json @@ -78,11 +78,16 @@ "id":"1", "name":"Choose Your Ticket", "description":"You can click one of the buttons below!", + "enableFooter":false, "footer":"Open Ticket v2.4.0 - I'm a footer!", + "enableThumbnail":false, "thumbnail":"https://www.example.com/catmemes/cat.png", + + "enableCustomColor":false, "color":"#ffffff", + "options":["general","apply"], "enableTicketExplaination":true, "enableMaxTicketsWarning":true diff --git a/core/api/api docs.md b/core/api/api docs.md deleted file mode 100644 index c8b0832..0000000 --- a/core/api/api docs.md +++ /dev/null @@ -1,56 +0,0 @@ -# OT-API Documentation -Extend open ticket with your own features! - -The api is made for EXTENDING open ticket, not replacing! So with this api you are not able to replace existing features! You can only add your own features! - -### DISCLAIMER: -Only edit or extend the code when you know what you are doing! - -If open ticket crashes while you change the code, then we can't help you! - -**DO IT ON YOUR OWN RISK!** - -## Events: -Everything in the `events.js` file! - -### onTicketOpen(...) -This function is executed when a new ticket is created. It happens after everything else is done, so the channel is already created! - -Parameters: -- user: `discord.User` -- channel: `discord.TextChannel` -- guild: `discord.Guild` -- date: `Date` -- ticketdata: `OT TicketData` or `false` (more info further down this document) - -### onTicketClose(...) -This function is executed when a ticket gets closed, it can happen multiple times with one ticket (because of reopen). - -Parameters: -- user: `discord.User` -- channel: `discord.TextChannel` -- guild: `discord.Guild` -- date: `Date` -- ticketdata: `OT TicketData` or `false` (more info further down this document) - -### onTicketDelete(...) -This function is executed when a ticket gets deleted, it can happen only 1 time. - -Parameters: -- user: `discord.User` -- channel: `discord.TextChannel` -- guild: `discord.Guild` -- date: `Date` -- ticketdata: `OT TicketData` or `false` (more info further down this document) - -### onTicketReopen(...) -... - -### onTranscriptCreation(...) -... - -### onCommand(...) -... - -### onReactionRole(...) -... \ No newline at end of file diff --git a/core/api/api.json b/core/api/api.json index ea2feef..03dd942 100644 --- a/core/api/api.json +++ b/core/api/api.json @@ -1,3 +1,46 @@ { - "version":"1.0.0" + "version":"1.0.0", + + + "disclaimer":"DISABLE doesn't work yet! Below this message!", + "disable":{ + "messages":{ + "ticketCreated":false, + "ticketClosed":false, + "ticketDeleted":false, + "ticketReopened":false + }, + "transcripts":{ + "server":false, + "dm":false + }, + "commands":{ + "text":{ + "help":false, + "msg":false, + "close":false, + "delete":false, + "reopen":false, + "new":false, + "add":false, + "remove":false, + "rename":false + }, + "slash":{ + "help":false, + "msg":false, + "close":false, + "delete":false, + "reopen":false, + "new":false, + "add":false, + "remove":false, + "rename":false + } + }, + "checkerjs":{ + "token":false, + "all":false + } + } } \ No newline at end of file diff --git a/core/api/modules/events.js b/core/api/modules/events.js index d56d181..b9efe7c 100644 --- a/core/api/modules/events.js +++ b/core/api/modules/events.js @@ -11,6 +11,11 @@ const storage = bot.storage const ticketOpenListeners = [] const ticketCloseListeners = [] const ticketDeleteListeners = [] +const ticketReopenListeners = [] + +const transcriptCreationListeners = [] +const commandListeners = [] +const reactionRoleListeners = [] /** * @@ -72,6 +77,51 @@ exports.onTicketDelete = (user,channel,guild,date,ticketdata) => { } +/** + * + * @param {discord.User} user + * @param {discord.TextChannel} channel + * @param {discord.Guild} guild + * @param {Date} date + * @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:Object|false}} ticketdata + */ + exports.onTicketReopen = (user,channel,guild,date,ticketdata) => { + //system + bot.errorLog.log("api","ticket reopened",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}]) + + ticketReopenListeners.forEach((func) => { + try { + func(user,channel,guild,date,ticketdata) + }catch{} + }) + +} + +/** + * + * @param {discord.Message[]} messages + * @param {discord.TextChannel} channel + * @param {discord.Guild} guild + * @param {Date} date + */ + exports.transcriptCreation = (messages,channel,guild,date) => { + //system + bot.errorLog.log("api","transcript created",[{key:"messages",value:JSON.stringify(messages)},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id}]) + + transcriptCreationListeners.forEach((func) => { + try { + func(messages,channel,guild,date) + }catch{} + }) + +} + +//onTicketAdd +//onTicketRemove +//command +//reactionrole +//error + //EVENT CALLBACKS: /** @@ -83,6 +133,14 @@ exports.onTicketDelete = (user,channel,guild,date,ticketdata) => { * @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:Object|false}} ticketdata */ +/** + * @callback TranscriptCreationEvent + * @param {discord.Message[]} messages + * @param {discord.TextChannel} channel + * @param {discord.Guild} guild + * @param {Date} date + */ + //EVENT EXPORT: /**@param {TicketActionEvent} callback */ @@ -100,8 +158,20 @@ const onTicketDelete = (callback) => { ticketDeleteListeners.push(callback) } +/**@param {TicketActionEvent} callback */ +const onTicketReopen = (callback) => { + ticketReopenListeners.push(callback) +} + +/**@param {TranscriptCreationEvent} callback */ +const onTranscriptCreation = (callback) => { + transcriptCreationListeners.push(callback) +} + exports.events = { - onTicketOpen:onTicketOpen, - onTicketClose:onTicketClose, - onTicketDelete:onTicketDelete + onTicketOpen, + onTicketClose, + onTicketDelete, + onTicketReopen, + onTranscriptCreation } \ No newline at end of file diff --git a/core/checker.js b/core/checker.js index 4a31ebd..6247236 100644 --- a/core/checker.js +++ b/core/checker.js @@ -41,7 +41,7 @@ exports.checker = async () => { } /**@param {String} value */ const checkToken = (value) => { - if (value.includes(" ") || value.length < 40 || value.length > 70){ + if (value.includes(" ") || value.length < 40 || value.length > 90){ createError("'auth_token' | your token is invalid") } } @@ -310,6 +310,28 @@ exports.checker = async () => { checkOption(option,"options/"+index) }) + + //discord check + /** + const discord = require("discord.js") + const {GatewayIntentBits,Partials} = discord + const client = new discord.Client({ + intents:[ + GatewayIntentBits.DirectMessages, + GatewayIntentBits.GuildInvites, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.Guilds, + GatewayIntentBits.MessageContent + ], + partials:[Partials.Channel,Partials.Message] + }) + await client.guilds.fetch() + if (!client.guilds.cache.find(g => g.id == config.server_id)){ + createError("'server_id' | i'm not in a server with this id!") + } + */ + //the end diff --git a/core/ticketCloser.js b/core/ticketCloser.js index 9197806..484c381 100755 --- a/core/ticketCloser.js +++ b/core/ticketCloser.js @@ -75,6 +75,7 @@ exports.NEWcloseTicket = async (member,channel,prefix,mode) => { if (!guild) return if (!permsChecker.command(user.id,guild.id)){ permsChecker.sendUserNoPerms(user) + return } //start delete proccess @@ -115,6 +116,7 @@ exports.NEWcloseTicket = async (member,channel,prefix,mode) => { if (!guild) return if (!permsChecker.command(user.id,guild.id)){ permsChecker.sendUserNoPerms(user) + return } } @@ -202,6 +204,7 @@ exports.NEWcloseTicket = async (member,channel,prefix,mode) => { if (!guild) return if (!permsChecker.command(user.id,guild.id)){ permsChecker.sendUserNoPerms(user) + return } //start delete proccess diff --git a/core/ticketMessageEmbed.js b/core/ticketMessageEmbed.js index 7e97d04..bcc9d39 100644 --- a/core/ticketMessageEmbed.js +++ b/core/ticketMessageEmbed.js @@ -33,11 +33,16 @@ exports.createEmbed = (id) => { //----------------------------------- //create the embed const embed = new discord.EmbedBuilder() - .setColor(data.color) .setTitle(data.name) if (data.enableFooter) embed.setFooter({text:data.footer}) if (data.enableThumbnail) embed.setThumbnail(data.thumbnail) + if (data.enableCustomColor){ + embed.setColor(data.color) + }else{ + embed.setColor(config.main_color) + } + var description = data.description if (data.enableTicketExplaination){ description = description+"\n\n__"+l.messages.chooseCategory+"__\n" diff --git a/core/ticketReopener.js b/core/ticketReopener.js index 55d002d..3ebd39f 100755 --- a/core/ticketReopener.js +++ b/core/ticketReopener.js @@ -92,6 +92,51 @@ module.exports = () => { .setEmoji("🔓") ) + /** + * + * @param {discord.Guild} guild + * @param {discord.TextBasedChannel} channel + * @param {discord.User} user + * @returns + */ + const reopenTicket = (guild,channel,user) => { + log("system","re-opened a ticket",[{key:"ticket",value:channel.name},{key:"user",value:user.tag}]) + + var permissionsArray = [] + + //set everyone allowed + if (config.system['has@everyoneaccess']){ + var everyoneAllowPerms = [pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel] + var everyoneDenyPerms = [] + }else{ + var everyoneAllowPerms = [] + var everyoneDenyPerms = [pfb.ViewChannel] + } + permissionsArray.push({ + id:guild.roles.everyone, + type:"role", + allow:everyoneAllowPerms, + deny:everyoneDenyPerms + }) + + channel.permissionOverwrites.cache.forEach((p) => { + if (p.type == "member"){ + permissionsArray.push({ + id:p.id, + type:"member", + allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel], + deny:[] + }) + } + }) + + channel.permissionOverwrites.set(permissionsArray) + + require("./api/modules/events").onTicketReopen(user,channel,guild,new Date(),{name:channel.name,status:"reopened",ticketOptions:false}) + } + + + //NORMAL WAY client.on("interactionCreate",(interaction) => { if (!interaction.isButton()) return @@ -126,38 +171,7 @@ module.exports = () => { interaction.message.edit({embeds:[embed],components:[closeRowNormal]}) - log("system","re-opened a ticket",[{key:"ticket",value:interaction.channel.name},{key:"user",value:interaction.user.tag}]) - - - var permissionsArray = [] - - //set everyone allowed - if (config.system['has@everyoneaccess']){ - var everyoneAllowPerms = [pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel] - var everyoneDenyPerms = [] - }else{ - var everyoneAllowPerms = [] - var everyoneDenyPerms = [pfb.ViewChannel] - } - permissionsArray.push({ - id:interaction.guild.roles.everyone, - type:"role", - allow:everyoneAllowPerms, - deny:everyoneDenyPerms - }) - - interaction.channel.permissionOverwrites.cache.forEach((p) => { - if (p.type == "member"){ - permissionsArray.push({ - id:p.id, - type:"member", - allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel], - deny:[] - }) - } - }) - - interaction.channel.permissionOverwrites.set(permissionsArray) + reopenTicket(interaction.guild,interaction.channel,interaction.user) }) @@ -193,97 +207,6 @@ module.exports = () => { interaction.message.edit({content:null,embeds:[embed],components:[closeRowNormal]}) - log("system","re-opened a ticket",[{key:"ticket",value:interaction.channel.name},{key:"user",value:interaction.user.tag}]) - - - var permissionsArray = [] - - //set everyone allowed - if (config.system['has@everyoneaccess']){ - var everyoneAllowPerms = [pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel] - var everyoneDenyPerms = [] - }else{ - var everyoneAllowPerms = [] - var everyoneDenyPerms = [pfb.ViewChannel] - } - permissionsArray.push({ - id:interaction.guild.roles.everyone, - type:"role", - allow:everyoneAllowPerms, - deny:everyoneDenyPerms - }) - - interaction.channel.permissionOverwrites.cache.forEach((p) => { - if (p.type == "member"){ - permissionsArray.push({ - id:p.id, - type:"member", - allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel], - deny:[] - }) - } - }) - - interaction.channel.permissionOverwrites.set(permissionsArray) - - //getID & send DM - interaction.channel.messages.fetchPinned().then(msglist => { - var firstmsg = msglist.last() - - if (firstmsg == undefined || firstmsg.author.id != client.user.id) return false - - const id = firstmsg.embeds[0].author.name - - if (!id) return false - - try{ - if (config.system.enable_DM_Messages){ - interaction.member.send({embeds:[bot.errorLog.custom(l.messages.reopenTicketDmTitle,l.messages.reopenTicketDmDescription,":ticket:",config.main_color)]}) - } - } - catch{log("system","can't send DM to member, member doesn't allow dm's")} - }) + reopenTicket(interaction.guild,interaction.channel,interaction.user) }) - - //DASHBOARD (comming soon!) - /** - * - * @param {discord.Guild} guild - * @param {discord.TextBasedChannel} channel - * @param {discord.User} user - * @returns - */ - const reopenTicket = (guild,channel,user) => { - log("system","re-opened a ticket",[{key:"ticket",value:channel.name},{key:"user",value:user.tag}]) - - var permissionsArray = [] - - //set everyone allowed - if (config.system['has@everyoneaccess']){ - var everyoneAllowPerms = [pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel] - var everyoneDenyPerms = [] - }else{ - var everyoneAllowPerms = [] - var everyoneDenyPerms = [pfb.ViewChannel] - } - permissionsArray.push({ - id:guild.roles.everyone, - type:"role", - allow:everyoneAllowPerms, - deny:everyoneDenyPerms - }) - - channel.permissionOverwrites.cache.forEach((p) => { - if (p.type == "member"){ - permissionsArray.push({ - id:p.id, - type:"member", - allow:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel], - deny:[] - }) - } - }) - - channel.permissionOverwrites.set(permissionsArray) - } } \ No newline at end of file diff --git a/core/transcript.js b/core/transcript.js index 2cb6468..0602079 100755 --- a/core/transcript.js +++ b/core/transcript.js @@ -44,6 +44,8 @@ const getUserInfo = (channel) => { exports.createTranscript = async (messagecollection,channel) => { const ticketuserdata = await getUserInfo(channel) + + require("./api/modules/events").transcriptCreation(messagecollection,channel,channel.guild,new Date()) try { const cd = new Date() const months = ["January","February","March","April","May","June","July","August","September","October","November","December"] @@ -57,8 +59,8 @@ exports.createTranscript = async (messagecollection,channel) => { 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") + const msgstats = "files: "+msg.attachments.size+", embeds: "+msg.embeds.length + filearray.push("["+timestamp.getDate()+"/"+timestamp.getMonth()+"/"+timestamp.getFullYear()+", "+timestamp.getHours()+":"+timestamp.getMinutes()+":"+timestamp.getSeconds()+"|"+msg.author.tag+"] ["+msgstats+"]\n"+content+"\n") }) if (filearray.length < 2) filearray.push("transcript is empty") diff --git a/package.json b/package.json index 7d41951..407b2d3 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "author": "DJdj Development", "license": "ISC", "dependencies": { + "axios": "^0.27.2", "chalk": "^5.0.1", "discord.js": "^14.0.2", "express": "^4.17.1",