diff --git a/core/api/api.js b/core/api/api.js new file mode 100644 index 0000000..54a7acc --- /dev/null +++ b/core/api/api.js @@ -0,0 +1,13 @@ +//base +const bot = require("../../index") + +exports.client = bot.client +exports.config = bot.config +exports.utils = { + log:bot.errorLog.log(), + storage:bot.storage +} + +//events +const events = require("./modules/events").events +exports.events = events \ No newline at end of file diff --git a/core/api/modules/custom.js b/core/api/modules/custom.js deleted file mode 100644 index 1ef099d..0000000 --- a/core/api/modules/custom.js +++ /dev/null @@ -1,9 +0,0 @@ -const discord = require("discord.js") -const client = require("../../../index").client - -module.exports = () => { - //this code will run when the bot starts! - - - -} \ No newline at end of file diff --git a/core/api/modules/events.js b/core/api/modules/events.js index 14601f2..d56d181 100644 --- a/core/api/modules/events.js +++ b/core/api/modules/events.js @@ -6,6 +6,12 @@ const log = bot.errorLog.log const l = bot.language const storage = bot.storage + +//EVENT API: +const ticketOpenListeners = [] +const ticketCloseListeners = [] +const ticketDeleteListeners = [] + /** * * @param {discord.User} user @@ -18,7 +24,11 @@ exports.onTicketOpen = (user,channel,guild,date,ticketdata) => { //system bot.errorLog.log("api","new ticket created",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}]) - //custom code + ticketOpenListeners.forEach((func) => { + try { + func(user,channel,guild,date,ticketdata) + }catch{} + }) } @@ -34,7 +44,11 @@ exports.onTicketClose = (user,channel,guild,date,ticketdata) => { //system bot.errorLog.log("api","ticket closed",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}]) - //custom code + ticketCloseListeners.forEach((func) => { + try { + func(user,channel,guild,date,ticketdata) + }catch{} + }) } @@ -50,6 +64,44 @@ exports.onTicketDelete = (user,channel,guild,date,ticketdata) => { //system bot.errorLog.log("api","ticket deleted",[{key:"userid",value:user.id},{key:"channelid",value:channel.id},{key:"guildid",value:guild.id},{key:"ticketdata",value:JSON.stringify(ticketdata)}]) - //custom code + ticketDeleteListeners.forEach((func) => { + try { + func(user,channel,guild,date,ticketdata) + }catch{} + }) +} + +//EVENT CALLBACKS: + +/** + * @callback TicketActionEvent + * @param {discord.User} user + * @param {discord.TextChannel} channel + * @param {discord.Guild} guild + * @param {Date} date + * @param {{name:String,status:"open"|"closed"|"deleted"|"reopened",ticketOptions:Object|false}} ticketdata + */ + +//EVENT EXPORT: + +/**@param {TicketActionEvent} callback */ +const onTicketOpen = (callback) => { + ticketOpenListeners.push(callback) +} + +/**@param {TicketActionEvent} callback */ +const onTicketClose = (callback) => { + ticketCloseListeners.push(callback) +} + +/**@param {TicketActionEvent} callback */ +const onTicketDelete = (callback) => { + ticketDeleteListeners.push(callback) +} + +exports.events = { + onTicketOpen:onTicketOpen, + onTicketClose:onTicketClose, + onTicketDelete:onTicketDelete } \ No newline at end of file diff --git a/core/api/pluginlauncher.js b/core/api/pluginlauncher.js new file mode 100644 index 0000000..3c5a34d --- /dev/null +++ b/core/api/pluginlauncher.js @@ -0,0 +1,26 @@ +const fs = require("fs") +const bot = require("../../index") +const log = bot.errorLog.log + +module.exports = () => { + const files = fs.readdirSync("./plugins") + + var totalcount = 0 + var successcount = 0 + var failcount = 0 + files.forEach((file) => { + if (file.endsWith(".plugin.js")){ + try { + require("../../plugins/"+file)() + + successcount++ + totalcount++ + }catch{ + failcount++ + totalcount++ + } + } + }) + + log("info","loaded plugins",[{key:"success",value:successcount},{key:"error",value:failcount},{key:"total",value:totalcount}]) +} \ No newline at end of file diff --git a/core/closebuttons.js b/core/closebuttons.js index 255a4f8..03e1d7c 100644 --- a/core/closebuttons.js +++ b/core/closebuttons.js @@ -167,7 +167,7 @@ module.exports = () => { } }) - await require("./ticketCloser").closeTicket(interaction,prefix,"close") + await require("./ticketCloser").NEWcloseTicket(interaction.member,interaction.channel,prefix,"close") closeTicketButtonChecker = false }) @@ -218,7 +218,7 @@ module.exports = () => { } }) - await require("./ticketCloser").closeTicket(interaction,prefix,"close") + await require("./ticketCloser").NEWcloseTicket(interaction.member,interaction.channel,prefix,"close") closeTicket1ButtonChecker = false }) @@ -270,7 +270,7 @@ module.exports = () => { } }) - await require("./ticketCloser").closeTicket(interaction,prefix,"delete") + await require("./ticketCloser").NEWcloseTicket(interaction.member,interaction.channel,prefix,"delete") deleteTicketButtonChecker = false }) @@ -325,7 +325,7 @@ module.exports = () => { } }) - await require("./ticketCloser").closeTicket(interaction,prefix,"deletenotranscript") + await require("./ticketCloser").NEWcloseTicket(interaction.member,interaction.channel,prefix,"deletenotranscript") deleteTicket1ButtonChecker = false }) } \ No newline at end of file diff --git a/core/errorLogSystem.js b/core/errorLogSystem.js index 7a11162..169f8b7 100644 --- a/core/errorLogSystem.js +++ b/core/errorLogSystem.js @@ -92,8 +92,8 @@ const normalLog = (debugString) => { * @param {String} message * @param {[{key:String,value:String}]} params */ -exports.log = async (ptype,message,params) => { - var type = ptype +exports.log = async (type,message,params) => { + var ptype = type const chalk = await loadChalk() var paramstring = "" if (params){ @@ -105,20 +105,20 @@ exports.log = async (ptype,message,params) => { } const parameters = params ? "("+paramstring+")" : " " - if (type == "command"){ + if (ptype == "command"){ console.log(chalk.green("[command] ")+message+" "+chalk.yellow(parameters)) - }else if (type == "info"){ + }else if (ptype == "info"){ console.log(chalk.blue("[info] ")+message+" "+chalk.yellow(parameters)) - }else if (type == "system"){ + }else if (ptype == "system"){ console.log(chalk.green("[system] ")+message+" "+chalk.yellow(parameters)) - }else if (type == "api"){ + }else if (ptype == "api"){ if (require("./api/modules/base").enableApiLogs == true){ console.log(chalk.red("[api v"+require("./api/api.json").version+"] ")+message+" "+chalk.yellow(parameters)) } - type = "api v"+require("./api/api.json").version + ptype = "api v"+require("./api/api.json").version } const cd = new Date() const months = ["January","February","March","April","May","June","July","August","September","October","November","December"] - normalLog("["+cd.getDate()+" "+months[cd.getMonth()-1]+" "+cd.getFullYear()+" - "+cd.getSeconds()+":"+cd.getMinutes()+":"+cd.getHours()+"] ["+type+"] "+message+" "+parameters) + normalLog("["+cd.getDate()+" "+months[cd.getMonth()-1]+" "+cd.getFullYear()+" - "+cd.getSeconds()+":"+cd.getMinutes()+":"+cd.getHours()+"] ["+ptype+"] "+message+" "+parameters) } \ No newline at end of file diff --git a/core/ticketCloser.js b/core/ticketCloser.js index 16b7f29..9197806 100755 --- a/core/ticketCloser.js +++ b/core/ticketCloser.js @@ -5,37 +5,63 @@ const config = bot.config const log = bot.errorLog.log const l = bot.language const storage = bot.storage +const permsChecker = require("./utils/permisssionChecker") + +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,interaction.channel) + + if (fileattachment == false){ + log("system","internal error: transcript is not created!") + interaction.channel.send({embeds:[bot.errorLog.serverError(l.errors.somethingWentWrongTranscript)]}) + return + } + + interaction.channel.send({content:"**"+l.messages.hereIsTheTranscript+"**",files:[fileattachment]}) + }) +} /** * - * @param {discord.Interaction} interaction + * @param {discord.GuildMember} member + * @param {discord.TextChannel} channel * @param {String} prefix * @param {"delete"|"close"|"deletenotranscript"} mode */ -exports.closeTicket = async (interaction,prefix,mode) => { +exports.NEWcloseTicket = async (member,channel,prefix,mode) => { + const guild = channel.guild + const user = member.user const chalk = await (await import("chalk")).default - const channelmessages = await interaction.channel.messages.fetch() - const channel = interaction.channel + const channelmessages = await channel.messages.fetch() - channelmessages.sweep((msgSweep) => { - return msgSweep.author.id == client.user.id - }) + //start code - /** - * @type {String} ticketuserarray - */ - const ticketuserarray = interaction.channel.name - const ticketusername = ticketuserarray.split(prefix)[1] + //remove ot bot from transcript + channelmessages.sweep((msgSweep) => {return msgSweep.author.id == client.user.id}) - var getuserID = storage.get("userTicketStorage",interaction.channel.id) + /**@type {String}*/ + const channelname = channel.name + const ticketOpenerChannelName = channelname.split(prefix)[1] + + //get user that opened the ticket + var getuserID = storage.get("userTicketStorage",channel.id) try { - var getusernameStep1 = client.users.cache.find(u => u.id === getuserID) + var ticketOpener = client.users.cache.find(u => u.id === getuserID) var isDatabaseError = false - }catch{ - var isDatabaseError = true - } + }catch{var isDatabaseError = true} - if (!getusernameStep1){ + if (!ticketOpener){ console.log(chalk.red("[database error]"),"something went wrong when getting the data in the database.\nNo panic, this error fixes itself!") var isDatabaseError = true } @@ -44,22 +70,22 @@ exports.closeTicket = async (interaction,prefix,mode) => { var deleteRequired = false if (mode == "delete"){ - const permsmember = client.guilds.cache.find(g => g.id == interaction.guild.id).members.cache.find(m => m.id == interaction.member.id) - if (config.main_adminroles.some((item)=>{return permsmember.roles.cache.has(item)}) == false && !permsmember.permissions.has("Administrator") && !permsmember.permissions.has("ManageGuild")){ - try { - return interaction.member.send({embeds:[bot.errorLog.noPermsMessage]}) - }catch{ - return interaction.channel.send({embeds:[bot.errorLog.noPermsMessage]}) - } - } + //delete + //check perms + if (!guild) return + if (!permsChecker.command(user.id,guild.id)){ + permsChecker.sendUserNoPerms(user) + } + + //start delete proccess deleteRequired = true - await interaction.channel.send({content:"**"+l.messages.gettingdeleted+"**"}) - log("system","deleted a ticket",[{key:"user",value:interaction.user.tag},{key:"ticket",value:interaction.channel.name}]) + await channel.send({content:"**"+l.messages.gettingdeleted+"**"}) + log("system","deleted a ticket",[{key:"user",value:user.tag},{key:"ticket",value:channel.name}]) if (!isDatabaseError) storage.set("ticketStorage",getuserID,Number(storage.get("ticketStorage",getuserID)) - 1) - //getID & send DM - await interaction.channel.messages.fetchPinned().then(msglist => { + //getID & send DM & send api event + await channel.messages.fetchPinned().then(msglist => { var firstmsg = msglist.last() if (firstmsg == undefined || firstmsg.author.id != client.user.id) return false @@ -67,7 +93,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { const ticketId = firstmsg.embeds[0].footer.text.split("Ticket Type: ")[1] const ticketData = require("./getoptions").getOptionsById("newT"+ticketId) - require("./api/modules/events").onTicketDelete(interaction.user,interaction.channel,interaction.guild,new Date(),{name:interaction.channel.name,status:"deleted",ticketOptions:ticketData}) + require("./api/modules/events").onTicketDelete(user,channel,guild,new Date(),{name:channel.name,status:"deleted",ticketOptions:ticketData}) if (!firstmsg.embeds[0].author) return false const id = firstmsg.embeds[0].author.name @@ -76,42 +102,40 @@ exports.closeTicket = async (interaction,prefix,mode) => { try{ if (config.system.enable_DM_Messages){ - interaction.member.send({embeds:[bot.errorLog.custom(l.messages.deletedTicketDmTitle,l.messages.deletedTicketDmDescription,":ticket:",config.main_color)]}) + member.send({embeds:[bot.errorLog.custom(l.messages.deletedTicketDmTitle,l.messages.deletedTicketDmDescription,":ticket:",config.main_color)]}) } } catch{log("system","can't send DM to member, member doesn't allow dm's")} }) }else if (mode == "close"){ - + //close + //check perms if (config.system.closeMode == "adminonly"){ - const permsmember = client.guilds.cache.find(g => g.id == interaction.guild.id).members.cache.find(m => m.id == interaction.member.id) - if (config.main_adminroles.some((item)=>{return permsmember.roles.cache.has(item)}) == false && !permsmember.permissions.has("Administrator") && !permsmember.permissions.has("ManageGuild")){ - try { - return interaction.member.send({embeds:[bot.errorLog.noPermsMessage]}) - }catch{ - return interaction.channel.send({embeds:[bot.errorLog.noPermsMessage]}) - } + if (!guild) return + if (!permsChecker.command(user.id,guild.id)){ + permsChecker.sendUserNoPerms(user) } } + //set new permissions var permissionArray = [] const pfb = discord.PermissionFlagsBits if (!isDatabaseError) permissionArray.push({ - id:getusernameStep1, + id:ticketOpener, type:"member", allow:[pfb.ViewChannel], deny:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages] }) permissionArray.push({ - id:interaction.guild.roles.everyone, + id:guild.roles.everyone, type:"role", deny:[pfb.AddReactions,pfb.AttachFiles,pfb.EmbedLinks,pfb.SendMessages,pfb.ViewChannel] }) - interaction.channel.permissionOverwrites.set(permissionArray) + channel.permissionOverwrites.set(permissionArray) //message var closeButtonRow = new discord.ActionRowBuilder() @@ -144,20 +168,20 @@ exports.closeTicket = async (interaction,prefix,mode) => { .setColor(config.main_color) .setTitle(":lock: "+l.messages.closedTitle+" :lock:") .setDescription(l.messages.closedDescription) - interaction.channel.send({embeds:[embed],components:[closeButtonRow]}) + channel.send({embeds:[embed],components:[closeButtonRow]}) - log("system","closed a ticket",[{key:"user",value:interaction.user.tag},{key:"ticket",value:interaction.channel.name}]) + log("system","closed a ticket",[{key:"user",value:user.tag},{key:"ticket",value:channel.name}]) - //getID & send DM - await interaction.channel.messages.fetchPinned().then(msglist => { + //getID & send DM & send api event + await channel.messages.fetchPinned().then(msglist => { var firstmsg = msglist.last() if (firstmsg == undefined || firstmsg.author.id != client.user.id) return false const ticketId = firstmsg.embeds[0].footer.text.split("Ticket Type: ")[1] const ticketData = require("./getoptions").getOptionsById("newT"+ticketId) - require("./api/modules/events").onTicketClose(interaction.user,interaction.channel,interaction.guild,new Date(),{name:interaction.channel.name,status:"closed",ticketOptions:ticketData}) + require("./api/modules/events").onTicketClose(user,channel,guild,new Date(),{name:channel.name,status:"closed",ticketOptions:ticketData}) const id = firstmsg.embeds[0].author.name @@ -165,7 +189,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { try{ if (config.system.enable_DM_Messages){ - interaction.member.send({embeds:[bot.errorLog.custom(l.messages.closedTicketDmTitle,l.messages.closedTicketDmDescription,":ticket:",config.main_color)]}) + member.send({embeds:[bot.errorLog.custom(l.messages.closedTicketDmTitle,l.messages.closedTicketDmDescription,":ticket:",config.main_color)]}) } } catch{log("system","can't send DM to member, member doesn't allow dm's")} @@ -173,22 +197,23 @@ exports.closeTicket = async (interaction,prefix,mode) => { }else if (mode == "deletenotranscript"){ + //delete with no transcript + //check perms + if (!guild) return + if (!permsChecker.command(user.id,guild.id)){ + permsChecker.sendUserNoPerms(user) + } - const permsmember = client.guilds.cache.find(g => g.id == interaction.guild.id).members.cache.find(m => m.id == interaction.member.id) - if (config.main_adminroles.some((item)=>{return permsmember.roles.cache.has(item)}) == false && !permsmember.permissions.has("Administrator") && !permsmember.permissions.has("ManageGuild")){ - interaction.channel.send({embeds:[bot.errorLog.noPermsMessage]}) - return - } - + //start delete proccess enableTranscript = false deleteRequired = true - interaction.channel.send({content:"**"+l.messages.gettingdeleted+"**"}) - log("system","deleted a ticket",[{key:"user",value:interaction.user.tag},{key:"ticket",value:interaction.channel.name}]) + channel.send({content:"**"+l.messages.gettingdeleted+"**"}) + log("system","deleted a ticket",[{key:"user",value:user.tag},{key:"ticket",value:channel.name}]) if (!isDatabaseError) storage.set("ticketStorage",getuserID,Number(storage.get("ticketStorage",getuserID)) - 1) - //getID & send DM - await interaction.channel.messages.fetchPinned().then(msglist => { + //getID & send DM & send api event + await channel.messages.fetchPinned().then(msglist => { var firstmsg = msglist.last() if (firstmsg == undefined || firstmsg.author.id != client.user.id) return false @@ -196,7 +221,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { const ticketId = firstmsg.embeds[0].footer.text.split("Ticket Type: ")[1] const ticketData = require("./getoptions").getOptionsById("newT"+ticketId) - require("./api/modules/events").onTicketDelete(interaction.user,interaction.channel,interaction.guild,new Date(),{name:interaction.channel.name,status:"deleted",ticketOptions:ticketData}) + require("./api/modules/events").onTicketDelete(user,channel,guild,new Date(),{name:channel.name,status:"deleted",ticketOptions:ticketData}) const id = firstmsg.embeds[0].author.name @@ -204,7 +229,7 @@ exports.closeTicket = async (interaction,prefix,mode) => { try{ if (config.system.enable_DM_Messages){ - interaction.member.send({embeds:[bot.errorLog.custom(l.messages.deletedTicketDmTitle,l.messages.deletedTicketDmDescription,":ticket:",config.main_color)]}) + member.send({embeds:[bot.errorLog.custom(l.messages.deletedTicketDmTitle,l.messages.deletedTicketDmDescription,":ticket:",config.main_color)]}) } } catch{log("system","can't send DM to member, member doesn't allow dm's")} @@ -224,11 +249,11 @@ exports.closeTicket = async (interaction,prefix,mode) => { const transcriptEmbed = new discord.EmbedBuilder() .setColor(config.main_color) .setTitle(l.messages.newTranscriptTitle) - .setAuthor({name:interaction.user.username,iconURL:interaction.user.displayAvatarURL({format:"png"})}) + .setAuthor({name:user.username,iconURL:user.displayAvatarURL({format:"png"})}) .setDescription(l.messages.newTranscriptDescription) - .setFooter({text:"ticket: "+ticketuserarray}) + .setFooter({text:"ticket: "+channelname}) - interaction.guild.channels.cache.find(c => c.id == config.system.transcript_channel).send({ + guild.channels.cache.find(c => c.id == config.system.transcript_channel).send({ embeds:[transcriptEmbed], files:[fileattachment] }) @@ -239,9 +264,9 @@ exports.closeTicket = async (interaction,prefix,mode) => { .setColor(config.main_color) .setTitle(l.messages.newTranscriptTitle) .setDescription(l.messages.newTranscriptDescription) - .setFooter({text:"ticket: "+ticketuserarray}) + .setFooter({text:"ticket: "+channelname}) - if (!isDatabaseError) getusernameStep1.send({ + if (!isDatabaseError) ticketOpener.send({ embeds:[transcriptEmbed], files:[fileattachment] }) @@ -253,31 +278,6 @@ exports.closeTicket = async (interaction,prefix,mode) => { setTimeout(() => {resolve(true)},7000) })} await timer() - await interaction.channel.delete() + await channel.delete() } -} - -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,interaction.channel) - - if (fileattachment == false){ - log("system","internal error: transcript is not created!") - interaction.channel.send({embeds:[bot.errorLog.serverError(l.errors.somethingWentWrongTranscript)]}) - return - } - - interaction.channel.send({content:"**"+l.messages.hereIsTheTranscript+"**",files:[fileattachment]}) - }) } \ No newline at end of file diff --git a/index.js b/index.js index 7d42019..ef2d67b 100644 --- a/index.js +++ b/index.js @@ -117,6 +117,9 @@ client.on('ready',async () => { process.exit(1) } } + + //load plugins + require("./core/api/pluginlauncher")() }) if (!isDev){ diff --git a/plugins/example.plugin.js b/plugins/example.plugin.js new file mode 100644 index 0000000..b000ddd --- /dev/null +++ b/plugins/example.plugin.js @@ -0,0 +1,15 @@ +const discord = require("discord.js") +const api = require("../core/api/api.js") + +module.exports = () => { + const {client,config,events,utils} = api + + //this code will run when the bot starts! + + events.onTicketOpen((user,channel,guild,date,ticketdata) => { + console.log("hello, someone opened a ticket!") + }) + + + +} \ No newline at end of file