diff --git a/README.md b/README.md index 554ee76..5326c58 100644 --- a/README.md +++ b/README.md @@ -47,40 +47,22 @@ To operate functionally the bot need the following : * **turnoff** : Turn off the bot. #### ~ Admin Level ~ * **bind** : Bind user thread to a channel. -* **configinfo** : Show a configuration information. -* **configuration** : Bot configuration. +* **config** : Show current bot config or info about each config. * **guilds** : List of guilds (servers) that have this bot. -* **reset** : Reset all configuration values. +* **reset** : Reset specific or all configuration values. * **set** : Set specific configuration value. #### ~ Moderator Level ~ * **aclose** : Anonymously close a user thread. * **areply** : Anonymously reply to a user thread. -* **block** : Block a user from creating new thread and replying to a thread. -* **blockinfo** : Information about a user's block. -* **blocklist** : List of blocked users. +* **block** : Block a user, show an info, or show list of blocked user(s). * **close** : Close a user thread. * **reply** : Reply to a user thread. -* **tag** : Send a saved response. -* **tagadd** : Add a saved response. -* **tagdelete** : Delete a saved response. -* **tagedit** : Edit a saved response. -* **taginfo** : Show a saved response information. -* **taglist** : Show all tag names. -* **threadinfo** : Show a user thread information. -* **threadlist** : Show all open threads. +* **tag** : Send, add, delete, edit, show an info or show list of saved response(s). +* **thread** : Show a user thread information or list of open thread(s). * **unblock** : Unblock user from creating new thread. #### ~ User level ~ * **commands** : List of all available commands according to your permission level. * **help** : Short instruction on how to create a new thread or info on a specific command. -* **helpchs** : 显示如何使用操作这个系统的说明. -* **helpcht** : 顯示如何使用操作這個系統的說明. -* **helpde** : Anleitung zur Verwendung des Bots anzeigen. -* **helpes** : Mostrar instrucciones sobre cómo usar el bot. -* **helpfr** : Afficher les instructions sur l'utilisation du bot. -* **helpkr** : 봇 사용 방법에 대한 지시 사항 표시. -* **helppt** : Mostrar instruções sobre como usar o bot. -* **helpru** : Показать инструкцию о том, как использовать бот. -* **helptr** : Bot kullanımı hakkında talimat göster. * **new** : Create new thread. * **ping** : Calculate bot latency. @@ -90,5 +72,8 @@ I made Discord server to test the bot, feel free to join at https://discord.gg/b ## Change Log -1. Migrate Database system from sqlite to replDB. -2. Due to replDB limitation, queue feature are removed and several other changes are made. +1. Events separated from index file make it easier for me to maintain or add stuff. +2. Permission check are now sentralized in commandHandler.js instead of in individual command file. +3. Config value are now checked before executing the command and will notify if it's invalid. +4. Several commands are now merged, use `help ` for more info. +5. More bot action logged in console for easier debugging. \ No newline at end of file diff --git a/commands/aclose.js b/commands/aclose.js index 4001c2b..66ec144 100644 --- a/commands/aclose.js +++ b/commands/aclose.js @@ -1,58 +1,69 @@ module.exports = { - name: 'aclose', - aliases: false, - level: 'Moderator', + name: "aclose", + aliases: ["ac"], + level: "Moderator", guildOnly: true, args: true, - usage: '[reason]-[note]', - description: 'Anonymously close a user thread.', + reqConfig: ["mainServerID", "threadServerID", "logChannelID"], // Configs needed to run this command. + usage: ["-[note]"], + description: "Anonymously close a user thread.", note: false, - async execute(param, message, args) { - const config = param.config; + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; const getEmbed = param.getEmbed; - const aclose = param.aclose; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`categoryID` and/or `logChannelID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - const notChannelEmbed = getEmbed.execute(param, config.error_color, "Invalid Channel", `This isn't thread channel.`); - - if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - return message.channel.send(noServerEmbed); - } else if(message.guild.id == config.threadServerID) { - // inside thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID or modRoleID empty - return message.channel.send(noAdminEmbed); - } else if(config.categoryID == "empty" || config.logChannelID == "empty") { - // categoryID or logChannelID empty - return message.channel.send(noChannelEmbed); - } else if (message.channel.parentID != config.categoryID || message.channel.id == config.logChannelID || message.channel.id == config.botChannelID) { - // adminRoleID, modRoleID, categoryID and logChannelID not empty - // the channel isn't under modmail category or it's a log channel or it's bot channel -_- - return message.channel.send(notChannelEmbed); - } else if(message.author.id == config.botOwnerID) { - // the channel is under modmail category, it's not a log channel, and it's not bot channel - return aclose.execute(param, message, args); - } else if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return aclose.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return aclose.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission, admin role, moderator role, and not in bot channel - return; - } else { - // user didn't have ADMINISTRATOR permission, admin role, nor moderator role - return message.channel.send(noPermEmbed); + const config = param.config; + const db = param.db; + const threadPrefix = param.dbPrefix.thread; + const updateActivity = param.updateActivity; + + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.fetch(mainServerID); + const threadServerID = config.threadServerID; + const threadServer = await client.guilds.fetch(threadServerID); + const logChannelID = config.logChannelID; + const logChannel = await threadServer.channels.fetch(logChannelID); + const author = message.author; + const channel = message.channel; + + const userID = channel.name.split("-").pop(); + const isThread = await db.get(threadPrefix + userID); + const addSpace = args.join(" "); + const deleteSeparator = addSpace.split(/-+/); + const reason = deleteSeparator.shift(); + const note = deleteSeparator.shift() || "empty"; + + const noThreadEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find any thread asociated with this channel."); + + if (!isThread) { + console.log("> Thread not found."); + return replyChannel.send(noThreadEmbed); + } + else { + const temp = isThread.split("-"); + temp.shift(); + const threadTitle = temp.join("-"); + const user = await client.users.fetch(userID); + const logDescription = `${threadTitle}\n**Reason** : ${reason}\n**Note** : ${note}`; + const userDescription = `${threadTitle}\n**Reason** : ${reason}`; + + let logEmbed; + const userDMEmbed = getEmbed.execute(param, "[Anonymous]", config.warning_color, "Thread Closed", userDescription, "", mainServer); + + if (user) { + logEmbed = getEmbed.execute(param, author, config.warning_color, "Thread Closed Anonymously", logDescription, "", user); + await user.send(userDMEmbed); + await logChannel.send(logEmbed); + } + else { + logEmbed = getEmbed.execute(param, author, config.warning_color, "Thread Closed Anonymously", logDescription, "", `Can't find user | ${userID}`); + await logChannel.send(logEmbed); } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + db.delete(threadPrefix + userID).then(() => console.log("> Thread closed anonymously.")); + await updateActivity.execute(param); + return channel.delete().then(() => console.log("> Channel deleted.")); } - } + }, }; diff --git a/commands/areply.js b/commands/areply.js index 8078602..dd1ed49 100644 --- a/commands/areply.js +++ b/commands/areply.js @@ -1,61 +1,88 @@ module.exports = { - name: 'areply', - aliases: false, - level: 'Moderator', + name: "areply", + aliases: ["ar"], + level: "Moderator", guildOnly: true, - // in case only attachment no message - args: false, - usage: '[reply message]', - description: 'Anonymously reply to a user thread.', + args: false, // in case there's only attachment with no message + reqConfig: ["mainServerID"], // Configs needed to run this command. + usage: ["[reply message]"], + description: "Anonymously reply to a user thread.", note: false, - async execute(param, message, args) { - const config = param.config; + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const MessageAttachment = param.MessageAttachment; + const client = param.client; const getEmbed = param.getEmbed; - const areply = param.areply; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`categoryID` and/or `logChannelID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - const notChannelEmbed = getEmbed.execute(param, config.error_color, "Invalid Channel", `This isn't thread channel.`); - const noArgsEmbed = param.getEmbed.execute(param, config.warning_color, "Missing Arguments", `You didn't provide any arguments nor attachments.`); - - if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - return message.channel.send(noServerEmbed); - } else if(message.guild.id == config.threadServerID) { - // inside thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID or modRoleID empty - return message.channel.send(noAdminEmbed); - } else if(config.categoryID == "empty" || config.logChannelID == "empty") { - // categoryID or logChannelID empty - return message.channel.send(noChannelEmbed); - } else if (message.channel.parentID != config.categoryID || message.channel.id == config.logChannelID || message.channel.id == config.botChannelID) { - // adminRoleID, modRoleID, categoryID and logChannelID not empty - // the channel isn't under modmail category or it's a log channel or it's bot channel -_- - return message.channel.send(notChannelEmbed); - } else if(!args.length && message.attachments.size == 0) { - // the channel is under modmail category, it's not a log channel, and it's not bot channel - return message.channel.send(noArgsEmbed); - } else if(message.author.id == config.botOwnerID) { - return areply.execute(param, message, args); - } else if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return areply.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return areply.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); + const config = param.config; + const db = param.db; + const threadPrefix = param.dbPrefix.thread; + const isMember = param.isMember; + const isBlocked = param.isBlocked; + + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.fetch(mainServerID); + const author = message.author; + + const userID = message.channel.name.split("-").pop(); + const isThread = await db.get(threadPrefix + userID); + const checkIsBlocked = await isBlocked.execute(param, author.id); + + const blockedEmbed = getEmbed.execute(param, "", config.error_color, "Blocked", "User blocked."); + const noDMEmbed = getEmbed.execute(param, "", config.error_color, "Not Sent", "User disabled Direct Message."); + const noUserEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find user in my collection."); + const noThreadEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find any thread asociated with this channel."); + + if (!isThread) { + console.log("> Thread not found."); + return replyChannel.send(noThreadEmbed); + } + else { + const checkIsMember = await isMember.execute(param, userID); + const notMemberEmbed = getEmbed.execute(param, "", config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); + + if (!checkIsMember) { + console.log("> The user isn't a main server member."); + return replyChannel.send(notMemberEmbed); + } + else if (checkIsBlocked) { + console.log("> The user is blocked."); + return replyChannel.send(blockedEmbed); } + else { + const member = await mainServer.members.fetch(userID); - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + if (!member) { + console.log("> Can't fetch user data."); + return replyChannel.send(noUserEmbed); + } + else { + const user = member.user; + const description = args.join(" "); + const userDMEmbed = getEmbed.execute(param, "[Anonymous]", config.sent_color, "Message Received", description, "", mainServer); + const threadChannelEmbed = getEmbed.execute(param, author, config.sent_color, "Message Sent Anonymously", description, "", user); + + try{ + await user.send(userDMEmbed); + } + catch (error) { + if(error.message == "Cannot send messages to this user") { + console.log("> Recipient's DM are disabled."); + return replyChannel.send(noDMEmbed); + } + } + await replyChannel.send(threadChannelEmbed); + if (message.attachments.size > 0) { + await message.attachments.forEach(async atch => { + const attachment = new MessageAttachment(atch.url); + await user.send(attachment); + await replyChannel.send(attachment); + }); + } + return message.delete().then(() => console.log("> Message deleted.")); + } + + } } - } + }, }; diff --git a/commands/bind.js b/commands/bind.js index 255118a..f270763 100644 --- a/commands/bind.js +++ b/commands/bind.js @@ -1,56 +1,72 @@ module.exports = { - name: 'bind', + name: "bind", aliases: false, - level: 'Admin', + level: "Admin", guildOnly: true, args: true, - usage: ' ', - description: 'Bind user thread to a channel.', - note: 'User presence isn\'t checked to enable binding user thread that are outside the server.\nOnly use under these circumtances : \n> There is an open thread from other bot.\n> The channel was accidentally deleted.\n> Category channel was accidentally deleted and changed.', - async execute(param, message, args) { - const config = param.config; + reqConfig: ["mainServerID", "threadServerID", "categoryID", "logChannelID"], // Configs needed to run this command. + usage: [" [thread title]"], + description: "Bind user thread to a channel.", + note: "\nOnly use under these circumtances : \n> There is an open thread from other bot.\n> The channel was accidentally deleted.\n> Category channel was accidentally deleted and changed.", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; const getEmbed = param.getEmbed; - const bind = param.bind; + const config = param.config; + const db = param.db; + const threadPrefix = param.dbPrefix.thread; + const updateActivity = param.updateActivity; + + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.fetch(mainServerID); + const threadServerID = config.threadServerID; + const threadServer = await client.guilds.fetch(threadServerID); + const categoryID = config.categoryID; + const logChannelID = config.logChannelID; + + const userID = args.shift(); + const channelID = args.shift(); + const dbKey = threadPrefix + userID; + let isThread = await db.get(dbKey); + const getChannel = await threadServer.channels.fetch(channelID); + const chUserID = getChannel.name.split("-").pop(); + const chIsThread = await db.get(threadPrefix + chUserID); + const member = await mainServer.members.fetch(userID); + const channelName = member ? member.user.tag.replace(/[^0-9a-z]/gi, "") + `-${userID}` : userID; - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`categoryID` and/or `logChannelID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` value is empty."); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `Binded <@${userID}> (\`${userID}\`) thread to <#${channelID}>.`); + const noChannelEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find that channel."); + const activeChannelEmbed = getEmbed.execute(param, "", config.error_color, "Error", "That's other user's thread channel."); + const notChannelEmbed = getEmbed.execute(param, "", config.error_color, "Invalid Channel", "That channel can't be a thread channel."); - if (message.author.id === config.botOwnerID) { - // bot owner - return bind.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - if(config.mainServerID == "empty" || config.threadServerID == "empty") { - return message.channel.send(noServerEmbed); - } else if(config.categoryID == "empty" || config.logChannelID == "empty") { - return message.channel.send(noChannelEmbed); - } else { - return bind.execute(param, message, args); - } - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - if(config.categoryID == "empty" || config.logChannelID == "empty") { - return message.channel.send(noChannelEmbed); - } else { - return bind.execute(param, message, args); - } - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + if(!getChannel) { + console.log("> Channel not found."); + return replyChannel.send(noChannelEmbed); + } + else if(chIsThread) { + console.log("> Other user thread channel."); + return replyChannel.send(activeChannelEmbed); + } + else if(getChannel.parentID != categoryID || channelID == categoryID || channelID == logChannelID) { + console.log("> Invalid channel."); + return replyChannel.send(notChannelEmbed); + } + else if(!isThread) { + await db.set(dbKey, `${channelID}-${args.join(" ") || "empty"}`); + await getChannel.setName(channelName); + await updateActivity.execute(param); + console.log("> Thread created."); + return replyChannel.send(successEmbed); + } + else { + isThread.split("-").shift(); + isThread = isThread.join("-"); + await db.set(dbKey, `${channelID}-${isThread}`); + await getChannel.setName(channelName); + await updateActivity.execute(param); + console.log("> Channel binded to a thread."); + return replyChannel.send(successEmbed); } - } + }, }; diff --git a/commands/block.js b/commands/block.js index 27cb573..29426fa 100644 --- a/commands/block.js +++ b/commands/block.js @@ -1,49 +1,107 @@ module.exports = { - name: 'block', - aliases: false, - level: 'Moderator', + name: "block", + aliases: ["b"], + level: "Moderator", guildOnly: true, args: true, - usage: ' [reason]', - description: 'Block a user from creating new thread and replying to a thread.', - note: 'User presence isn\'t checked to enable blocking users that are outside the server.', - async execute(param, message, args) { + reqConfig: false, // Configs needed to run this command. + usage: [" [reason]", " ", " [page number]"], + description: "Block a user, show an info, or show list of blocked user(s).", + note: "User presence isn't checked to enable blocking users that are outside the server.", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const config = param.config; + const db = param.db; + const blockPrefix = param.dbPrefix.block; const getEmbed = param.getEmbed; - const block = param.block; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return block.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return block.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return block.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return block.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + + const author = message.author; + const modID = author.id; + const firstArg = args.shift().toLowerCase(); + + switch(firstArg) { + case "i": // fallthrough + case "?": // fallthrough + case "info": { + const userID = args.shift(); + const notFoundEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `<@${userID}> (\`${userID}\`) isn't blocked.`); + const blockData = await db.get(blockPrefix + userID); + + if(blockData) { + const temp = blockData.split("-"); + const blockModID = temp.shift(); + const reason = temp.join("-"); + const data = []; + data.push(`**User** : <@${userID}> [\`${userID}\`]`); + data.push(`**Moderator** : <@${modID}> [\`${blockModID}\`]`); + data.push(`**Reason** : ${reason}`); + const infoEmbed = getEmbed.execute(param, "", config.info_color, "Block Info", data.join("\n")); + replyChannel.send(infoEmbed); + } + else { + console.log("> Data not found."); + replyChannel.send(notFoundEmbed); + } + break; + } + case "l": // fallthrough + case "list": { + let pageNumber = args.shift(); + + const blocklist = await db.list(blockPrefix); + let pages = Math.floor(blocklist.length / 20); + if (blocklist.length % 20 != 0) { + // add 1 number of pages if residual quotient is not 0 (15%10=5 -> 5 > 0) + pages += 1; + } + if (blocklist.length == 0) { + pageNumber = 0; + } + else if(!pageNumber || isNaN(pageNumber) || pageNumber <= 0) { + // user didn't gave input or input is not a number or input is below or same as 0 + pageNumber = 1; + } + else if(pageNumber > pages) { + // input is higher than the number of pages + pageNumber = pages; + } + + const listArray = blocklist.map(block =>`🔸 <@${block.slice(blockPrefix.length)}> (\`${block.slice(blockPrefix.length)}\`)`); + const firstIndex = Math.abs((pageNumber - 1) * 20); + let listString = listArray.slice(firstIndex, firstIndex + 20).join("\n") || "`List empty.`"; + if (pages > 1) { + listString += `\n\`Page ${pageNumber} from ${pages} pages\``; + } + else { + listString += `\n\`Page ${pageNumber} from ${pages} page\``; + } + + const listEmbed = getEmbed.execute(param, "", config.info_color, "Blocked Users", listString); + replyChannel.send(listEmbed); + break; + } + default: { + const userID = firstArg; + const reason = args.join(" ") || "empty"; + + const duplicatedEmbed = getEmbed.execute(param, "", config.error_color, "Duplicated", `<@${userID}> (\`${userID}\`) already blocked.`); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `Succesfully block <@${userID}> (\`${userID}\`).`); + + const dbKey = blockPrefix + userID; + const isBlocked = await db.get(dbKey); + if(isBlocked) { + console.log(`> User [${userID}] is already blocked.`); + return replyChannel.send(duplicatedEmbed); + } + else { + db.set(dbKey, `${modID}-${reason}`).then(() => { + console.log(`> Blocked [${userID}].`); + return replyChannel.send(successEmbed); + }); + } + break; + } } - } + }, }; diff --git a/commands/blockinfo.js b/commands/blockinfo.js deleted file mode 100644 index 13c45fc..0000000 --- a/commands/blockinfo.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'blockinfo', - aliases: false, - level: 'Moderator', - guildOnly: true, - args: true, - usage: '', - description: 'Information about a user\'s block.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const blockinfo = param.blockinfo; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return blockinfo.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return blockinfo.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return blockinfo.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return blockinfo.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/blocklist.js b/commands/blocklist.js deleted file mode 100644 index 235101e..0000000 --- a/commands/blocklist.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'blocklist', - aliases: false, - level: 'Moderator', - guildOnly: true, - args: false, - usage: '[page number]', - description: 'List of blocked users.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const blocklist = param.blocklist; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return blocklist.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return blocklist.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return blocklist.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return blocklist.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/close.js b/commands/close.js index 1892712..3de1330 100644 --- a/commands/close.js +++ b/commands/close.js @@ -1,57 +1,69 @@ module.exports = { - name: 'close', - aliases: false, - level: 'Moderator', + name: "close", + aliases: ["c"], + level: "Moderator", guildOnly: true, args: true, - usage: '[reason]-[note]', - description: 'Close a user thread.', + reqConfig: ["mainServerID", "threadServerID", "logChannelID"], // Configs needed to run this command. + usage: ["[reason]-[note]"], + description: "Close a user thread.", note: false, - async execute(param, message, args) { - const config = param.config; + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; const getEmbed = param.getEmbed; - const close = param.close; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`categoryID` and/or `logChannelID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - const notChannelEmbed = getEmbed.execute(param, config.error_color, "Invalid Channel", `This isn't thread channel.`); - - if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - return message.channel.send(noServerEmbed); - } else if(message.guild.id == config.threadServerID) { - // inside thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID or modRoleID empty - return message.channel.send(noAdminEmbed); - } else if(config.categoryID == "empty" || config.logChannelID == "empty") { - // categoryID or logChannelID empty - return message.channel.send(noChannelEmbed); - } else if (message.channel.parentID != config.categoryID || message.channel.id == config.logChannelID || message.channel.id == config.botChannelID) { - // adminRoleID, modRoleID, categoryID and logChannelID not empty - // the channel isn't under modmail category or it's a log channel or it's bot channel -_- - return message.channel.send(notChannelEmbed); - } else if(message.author.id == config.botOwnerID) { - // the channel is under modmail category, it's not a log channel, and it's not bot channel - return close.execute(param, message, args); - } else if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return close.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return close.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); + const config = param.config; + const db = param.db; + const threadPrefix = param.dbPrefix.thread; + const updateActivity = param.updateActivity; + + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.cache.get(mainServerID); + const threadServerID = config.threadServerID; + const threadServer = await client.guilds.cache.get(threadServerID); + const logChannelID = config.logChannelID; + const logChannel = await threadServer.channels.cache.get(logChannelID); + const author = message.author; + const channel = message.channel; + + const userID = channel.name.split("-").pop(); + const isThread = await db.get(threadPrefix + userID); + const addSpace = args.join(" "); + const deleteSeparator = addSpace.split(/-+/); + const reason = deleteSeparator.shift(); + const note = deleteSeparator.shift() || "empty"; + + const noThreadEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find any thread asociated with this channel."); + + if (!isThread) { + console.log("> Thread not found."); + return replyChannel.send(noThreadEmbed); + } + else { + const temp = isThread.split("-"); + temp.shift(); + const threadTitle = temp.join("-"); + const user = await client.users.cache.get(userID); + const logDescription = `${threadTitle}\n**Reason** : ${reason}\n**Note** : ${note}`; + const userDescription = `${threadTitle}\n**Reason** : ${reason}`; + + let logEmbed; + const userDMEmbed = getEmbed.execute(param, author, config.warning_color, "Thread Closed", userDescription, "", mainServer); + + if (user) { + logEmbed = getEmbed.execute(param, author, config.warning_color, "Thread Closed", logDescription, "", user); + await user.send(userDMEmbed); + await logChannel.send(logEmbed); + } + else { + logEmbed = getEmbed.execute(param, author, config.warning_color, "Thread Closed Anonymously", logDescription, "", `Can't find user | ${userID}`); + await logChannel.send(logEmbed); } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + db.delete(threadPrefix + userID).then(() => console.log("> Thread closed.")); + await updateActivity.execute(param); + return channel.delete().then(() => console.log("> Channel deleted.")); } - } + }, }; diff --git a/commands/commands.js b/commands/commands.js index 31e5c0a..e35ede4 100644 --- a/commands/commands.js +++ b/commands/commands.js @@ -1,103 +1,60 @@ module.exports = { - name: 'commands', - aliases: ['cmd'], - level: 'User', + name: "commands", + aliases: ["cmd"], + level: "User", guildOnly: false, args: false, + reqConfig: false, // Configs needed to run this command. usage: false, - description: 'List of all available commands according to your permission level.', + description: "List of all available commands according to your permission level.", note: false, - async execute(param, message, args) { - const Discord = param.Discord; + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const config = param.config; const getEmbed = param.getEmbed; const commands = param.client.commands; + const description = `Use \`${config.prefix}help [command name]\` to get information for each command.`; + // collections - const ownerCollection = commands.filter(command => command.level === 'Owner'); - const ownerLevel = ownerCollection.map(command => `**${command.name}** : ${command.description}`).join('\n'); - const adminCollection = commands.filter(command => command.level === 'Admin'); - const adminLevel = adminCollection.map(command => `**${command.name}** : ${command.description}`).join('\n'); - const moderatorCollection = commands.filter(command => command.level === 'Moderator'); - const moderatorLevel = moderatorCollection.map(command => `**${command.name}** : ${command.description}`).join('\n'); - const userCollection = commands.filter(command => command.level === 'User'); - const userLevel = userCollection.map(command => `**${command.name}** : ${command.description}`).join('\n'); + const ownerCollection = commands.filter(command => command.level === "Owner"); + const ownerLevel = ownerCollection.map(command => `**${command.name}** : ${command.description}`).join("\n"); + const adminCollection = commands.filter(command => command.level === "Admin"); + const adminLevel = adminCollection.map(command => `**${command.name}** : ${command.description}`).join("\n"); + const moderatorCollection = commands.filter(command => command.level === "Moderator"); + const moderatorLevel = moderatorCollection.map(command => `**${command.name}** : ${command.description}`).join("\n"); + const userCollection = commands.filter(command => command.level === "User"); + const userLevel = userCollection.map(command => `**${command.name}** : ${command.description}`).join("\n"); - // embeds - const ownerEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("Commands") - .setDescription(`Use \`${config.prefix}help [command name]\` to get information for each command.`) - .addField("~ Owner Level ~", ownerLevel || "empty") - .addField("~ Admin Level ~", adminLevel || "empty") - .addField("~ Moderator Level ~", moderatorLevel || "empty") - .addField("~ User level ~", userLevel || "empty") - .setThumbnail(client.user.avatarURL) - .setFooter(client.user.tag, client.user.avatarURL()) - .setTimestamp(); - const adminEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("Command List") - .setDescription(`Use \`${config.prefix}help [command name]\` to get information for each command.`) - .addField("~ Admin Level ~", adminLevel || "empty") - .addField("~ Moderator Level ~", moderatorLevel || "empty") - .addField("~ User level ~", userLevel || "empty") - .setThumbnail(client.user.avatarURL) - .setFooter(client.user.tag, client.user.avatarURL()) - .setTimestamp(); - const moderatorEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("Command List") - .setDescription(`Use \`${config.prefix}help [command name]\` to get information for each command.`) - .addField("~ Moderator Level ~", moderatorLevel || "empty") - .addField("~ User level ~", userLevel || "empty") - .setThumbnail(client.user.avatarURL) - .setFooter(client.user.tag, client.user.avatarURL()) - .setTimestamp(); - const userEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("Command List") - .setDescription(`Use \`${config.prefix}help [command name]\` to get information for each command.`) - .addField("~ User level ~", userLevel || "empty") - .setThumbnail(client.user.avatarURL) - .setFooter(client.user.tag, client.user.avatarURL()) - .setTimestamp(); + // fields + const ownerField = `~ Owner Level ~;${ownerLevel || "empty"}`; + const adminField = `~ Admin Level ~;${adminLevel || "empty"}`; + const moderatorField = `~ Moderator Level ~;${moderatorLevel || "empty"}`; + const userField = `~ User Level ~;${userLevel || "empty"}`; - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); + // embeds + const ownerEmbed = getEmbed.execute(param, client.user, config.info_color, "Command List", description, [ownerField, adminField, moderatorField, userField], "", client.user.displayAvatarURL()); + const adminEmbed = getEmbed.execute(param, client.user, config.info_color, "Command List", description, [adminField, moderatorField, userField], "", client.user.displayAvatarURL()); + const moderatorEmbed = getEmbed.execute(param, client.user, config.info_color, "Command List", description, [moderatorField, userField], "", client.user.displayAvatarURL()); + const userEmbed = getEmbed.execute(param, client.user, config.info_color, "Command List", description, [userField], "", client.user.displayAvatarURL()); - if (message.author.id === config.botOwnerID) { - // bot owner - return message.channel.send(ownerEmbed); - } else if(message.guild == null) { - // Direct Message not bot owner - return message.channel.send(userEmbed); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return message.channel.send(adminEmbed) - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID or modRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return message.channel.send(adminEmbed); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return message.channel.send(moderatorEmbed); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission, admin role, nor moderator role - return; - } else { - return message.channel.send(userEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(userEmbed); + if (param.isOwner) { + // Bot owner + return replyChannel.send(ownerEmbed); + } + else if(param.isAdmin) { + // Admin + return replyChannel.send(adminEmbed); + } + else if(param.isModerator) { + // Moderator + return replyChannel.send(moderatorEmbed); + } + else { + // User/DM + return replyChannel.send(userEmbed); } - } + }, }; diff --git a/commands/config.js b/commands/config.js index 34c3d8a..46fc122 100644 --- a/commands/config.js +++ b/commands/config.js @@ -1,47 +1,172 @@ module.exports = { - name: 'config', - aliases: ['configuration', 'settings'], - level: 'Admin', + name: "config", + aliases: ["configuration", "settings", "cfg"], + level: "Admin", guildOnly: true, args: false, - usage: false, - description: 'Bot configuration.', - note: 'If [mainServerID] and/or [threadServerID] config isn\'t empty, only administrator in that server can use this command.', - async execute(param, message, args) { - const config = param.config; + reqConfig: false, // Configs needed to run this command. + usage: [" "], + description: "Show current bot config or info about each config.", + note: "If [mainServerID] and/or [threadServerID] config isn't empty, only administrator in that server can use this command.", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; const getEmbed = param.getEmbed; - const configFn = param.configFn; - - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` value is empty."); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const configEmbed = await configFn.execute(param); - - if (message.author.id === config.botOwnerID) { - // bot owner - return message.channel.send(configEmbed); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return message.channel.send(configEmbed); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); + const config = param.config; + const configKeys = Object.keys(config);// getting the name of each config (prefix, botOwnerID, etc) + + const firstArg = args.shift(); + + if(!firstArg || (firstArg.toLowerCase() !== "info" && firstArg.toLowerCase() !== "i" && firstArg !== "?")) { + const botConfig = []; + const serverConfig = []; + const embedColorConfig = []; + // As separator for server related config and bot config. + const maintenanceIndex = configKeys.indexOf("maintenance"); + // As separator for server related config and embed color config. + const info_colorIndex = configKeys.indexOf("info_color"); + + for (let i = 0; i < configKeys.length; i++) { + const confName = configKeys[i]; + const confValue = config[confName]; + if(i <= maintenanceIndex) { + botConfig.push(`${confName} : \`${confValue}\``); + } + else if(i > maintenanceIndex && i < info_colorIndex) { + if (confName.match(/role/i)) { + serverConfig.push(`${confName} : \`${confValue}\` ~ <@&${confValue}>`); + } + else if (confName.match(/channel/i)) { + serverConfig.push(`${confName} : \`${confValue}\` ~ <#${confValue}>`); + } + else { + serverConfig.push(`${configKeys[i]} : \`${config[configKeys[i]]}\``); + } + } + else if(i >= info_colorIndex && i < configKeys.length) { + embedColorConfig.push(`${configKeys[i]} : \`${config[configKeys[i]]}\``); + } + } + + // fields + const botField = `~ Bot ~;${botConfig.join("\n")}`; + const serverField = `~ Server ~;${serverConfig.join("\n")}`; + const colorField = `~ Embed Color ~;${embedColorConfig.join("\n")}`; + + const configEmbed = param.getEmbed.execute(param, client.user, config.info_color, "Configuration", "", [botField, serverField, colorField], "", client.user.displayAvatarURL()); + return replyChannel.send(configEmbed); + } + else { + const configName = args.shift(); + if (!configName) { + const noArgsEmbed = param.getEmbed.execute(param, "", config.warning_color, "Missing Arguments", `**Usage** : \`${config.prefix}${this.name} ${this.usage[0]}\``); + console.log("> Missing Arguments."); + return replyChannel.send(noArgsEmbed); } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return message.channel.send(configEmbed); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); + + const isConfig = await configKeys.includes(configName); + if (!isConfig) { + const configList = configKeys.map(conf => `\`${conf}\``).join(", "); + const noConfigEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `Couldn't find config named \`${configName}\`.\nAvailable names : ${configList}`); + console.log("> Invalid config name."); + return replyChannel.send(noConfigEmbed); } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + + const configData = []; + switch (configName) { + case "prefix": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : To differentiate between a command and non command."); + configData.push("**Requirements** : \n`> Any input that didn't have [space] as it'll be ignored.`"); + break; + case "botOwnerID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : An owner of this bot can use any commands anywhere."); + configData.push("**Requirements** : \n`> Only bot owner can change this value.\n> Input can't be empty.`"); + break; + case "cooldown": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Cooldown for each commands (in seconds)."); + configData.push("**Requirements** : \n`> Any number that's greater or equal to zero.\n> Input can't be empty.`"); + break; + case "maintenance": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Maintenance mode toggle. Config changed according previous value."); + break; + case "mainServerID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : The server that is used by users who will use this bot to ask moderators."); + configData.push("**Requirements** : \n`> Any server that have this bot.\n> Value can be same as [threadServerID].`"); + break; + case "threadServerID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : The server where thread channels will be on."); + configData.push("**Requirements** : \n`> Any server that have this bot.\n> Value can be same as [mainServerID].`"); + break; + case "categoryID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Category channel where thread channels will be created."); + configData.push("**Requirements** : \n`> Any category channel that are inside thread server.\n> [threadServerID] value can't be empty.`"); + configData.push("**Note** : To understand what category channel is, check this [link](https://support.discordapp.com/hc/en-us/articles/115001580171-Channel-Categories-101)."); + configData.push("`ps. Discord.js treat it as channel that's why i use this term too.`"); + break; + case "logChannelID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Channel where thread logs will be sent."); + configData.push("**Requirements** : \n`> Any channel inside thread server.\n> [threadServerID] value can't be empty.`"); + break; + case "adminRoleID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Role that will have administrator permission level."); + configData.push("**Requirements** : \n`> Any role inside thread server.\n> [threadServerID] value can't be empty.`"); + break; + case "modRoleID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Role that will have moderator permission level."); + configData.push("**Requirements** : \n`> Any role inside thread server.\n> [threadServerID] value can't be empty.`"); + break; + case "mentionedRoleID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : The role that will be mentioned on new thread."); + configData.push("**Requirements** : \n`> Can be empty (no one mentioned).\n> Any role at thread server including here and everyone [set mentionedRoleID everyone].`"); + break; + case "botChannelID": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Channel where user can only use to execute commands (any commands in other channels will be ignored except help commands)."); + configData.push("**Requirements** : \n`> Can be empty (everyone can use any commands anywhere).\n> Any channels inside main server.\n> Shouldn't be a category channel (Discord.js treat categories as a channel too).`"); + break; + case "info_color": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Color used for any information related embeds."); + configData.push("**Requirements** : \n`> Hex code color input.`"); + break; + case "warning_color": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Color used for any warning related embeds."); + configData.push("**Requirements** : \n`> Hex code color input.`"); + break; + case "error_color": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Color used for any error related embeds."); + configData.push("**Requirements** : \n`> Hex code color input.`"); + break; + case "sent_color": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Color used for any message sent on threads related embeds."); + configData.push("**Requirements** : \n`> Hex code color input.`"); + break; + case "received_color": + configData.push(`**Name** : ${configName}`); + configData.push("**Description** : Color used for any message received on threads related embeds."); + configData.push("**Requirements** : \n`> Hex code color input.`"); + break; + default: + configData.push("`Information is still not available.`"); + } + + const dataEmbed = await getEmbed.execute(param, "", config.info_color, "Configuration Information", configData.join("\n")); + return replyChannel.send(dataEmbed); } - } + }, }; diff --git a/commands/configinfo.js b/commands/configinfo.js deleted file mode 100644 index 62348ce..0000000 --- a/commands/configinfo.js +++ /dev/null @@ -1,46 +0,0 @@ -module.exports = { - name: 'configinfo', - aliases: ['chelp'], - level: 'Admin', - guildOnly: true, - args: true, - usage: '[config name]', - description: 'Show a configuration information.', - note: 'Config name case is sensitive (upper case and lower case are different).', - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const configinfo = param.configinfo; - - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` value is empty."); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return await configinfo.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return await configinfo.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return await configinfo.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/guilds.js b/commands/guilds.js index 3ae0b69..27aa332 100644 --- a/commands/guilds.js +++ b/commands/guilds.js @@ -1,48 +1,23 @@ module.exports = { - name: 'guilds', - aliases: ['servers', 'serverlist'], - level: 'Admin', + name: "guilds", + aliases: ["servers", "serverlist"], + level: "Admin", guildOnly: true, args: false, + reqConfig: false, // Configs needed to run this command. usage: false, - description: 'List of guilds (servers) that have this bot.', - note: 'If [mainServerID] and/or [threadServerID] config isn\'t empty, only administrator in that server can use this command.', - async execute(param, message, args) { + description: "List of guilds (servers) that have this bot.", + note: "If [mainServerID] and/or [threadServerID] config isn't empty, only administrator in that server can use this command.", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const config = param.config; const getEmbed = param.getEmbed; - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID`` value is empty."); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); const guildList = client.guilds.cache.map(guild => `[**${guild.name}**] (\`${guild.id}\`)`).join("\n") || "This bot hasn't joined any guild yet."; - const listEmbed = getEmbed.execute(param, config.info_color, "Guilds", guildList); + const listEmbed = getEmbed.execute(param, "", config.info_color, "Guilds", guildList); - if (message.author.id === config.botOwnerID) { - // bot owner - return message.channel.send(listEmbed); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return message.channel.send(listEmbed); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return message.channel.send(listEmbed); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } + return replyChannel.send(listEmbed); + }, }; diff --git a/commands/help.js b/commands/help.js index 2fd6bee..cb144b1 100644 --- a/commands/help.js +++ b/commands/help.js @@ -1,46 +1,106 @@ module.exports = { - name: 'help', - aliases: false, - level: 'User', + name: "help", + aliases: ["h", "?", "助攻", "helpch", "hilfe", "helpde", "ayuda", "helpes", "aide", "helpfr", "도움", "helpkr", "ajuda", "helppt", "помощь", "helpru", "yardım", "helptr"], + level: "User", guildOnly: false, args: false, - usage: '[command name]', - description: 'Short instruction on how to create a new thread or info on a specific command.', - note: 'Command name case is insensitive (upper case and lower case are same).', - async execute(param, message, args) { + reqConfig: false, // Configs needed to run this command. + usage: [""], + description: "Short instruction on how to create a new thread or info on a specific command.", + note: "Command name case is insensitive (upper case and lower case are same).", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const data = []; const commands = param.client.commands; const config = param.config; const prefix = config.prefix; const getEmbed = param.getEmbed; + const commandName = param.cmdName; + replyChannel = message.channel; - const helpEmbed = getEmbed.execute(param, config.info_color, "Instruction", `In Direct Message, use \`${prefix}new Your thread title here\` to create a new thread (**Thread Created!** will be displayed). You don't need to use any command after your thread created. Describe your issue afterward.`); - const notCmdEmbed = getEmbed.execute(param, config.warning_color, "Not a Command", `That's not a valid command name or alias.\nUse \`${prefix}commands\` to show available commands.`); + const notCmdEmbed = getEmbed.execute(param, "", config.warning_color, "Not a Command", `That's not a valid command name or alias.\nUse \`${prefix}commands\` to show available commands.`); + let helpEmbed; if(!args.length) { - return message.channel.send(helpEmbed); + switch(commandName) { + case "助攻" : // fallthrough + case "helpch" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "指令", `在“直接消息”中,使用\`${prefix}new 您的线程标题在这里\`创建一个新线程(显示“**Thread Created!**”)。创建线程后,无需使用任何命令。然后描述你的问题。`); + break; + } + case "hilfe" : // fallthrough + case "helpde" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "Anweisung", `In Direktnachrichten, Verwenden Sie \`${prefix}new Dein Threadtitel hier\`, um einen neuen Thread zu erstellen (es wird **Thread Created!** Angezeigt). Sie müssen nach dem Erstellen Ihres Threads keinen Befehl mehr verwenden. Beschreiben Sie anschließend Ihr Problem.`); + break; + } + case "ayuda" : // fallthrough + case "helpes" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "Instrucción", `En un mensaje privado, utilice \`${prefix}new el tema de su problema\` para crear un nuevo ticket (se muestra **Thread Created!**). No necesita usar ningún comando después de la creación del ticket. Luego describe su problema.`); + break; + } + case "aide" : // fallthrough + case "helpfr" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "Instruction", `En message privé, utilisez \`${prefix}new le sujet de votre problème\` pour créer un nouveau ticket (**Thread Created!** s'affiche). Vous n'avez pas besoin d'utiliser de commande après la création du ticket. Décrivez ensuite votre problème.`); + break; + } + case "도움" : // fallthrough + case "helpkr" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "훈령", `직접 메시지에서\`${prefix}new 스레드 제목은 여기\`를 사용하여 새 스레드를 만듭니다 (**Thread Created!**가 표시됨). 스레드가 작성된 후 명령을 사용할 필요가 없습니다. 그런 다음 문제를 설명하십시오.`); + break; + } + case "ajuda" : // fallthrough + case "helppt" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "Instrução", `Na mensagem direta, usar \`${prefix}new Título do assunto\` para criar um novo tópico (**Thread Created!** é exibido). Não precisa de usar nenhum comando depois de criar o seu tópico. Em seguida, descreva o seu problema.`); + break; + } + case "помощь" : // fallthrough + case "helpru" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "инструкция", `В прямом сообщении используйте \`${prefix}new Ваше название темы здесь\`, чтобы создать новую тему (**Thread Created!** Отображается). Вам не нужно использовать какую-либо команду после создания вашего потока. Тогда опишите свою проблему.`); + break; + } + case "yardım" : // fallthrough + case "helptr" : { + helpEmbed = getEmbed.execute(param, "", config.info_color, "Talimat", `Doğrudan Mesaj'da, yeni bir bilet oluşturmak için \`${prefix}new konuyu buraya yazın\` yazın ve gönderin (**Thread Created!** görüntülenir). Konuyu oluşturduktan sonra herhangi bir komut kullanmanıza gerek yoktur. Daha sonra sorununuzu açıklayın.`); + break; + } + default: { + helpEmbed = getEmbed.execute(param, "", config.info_color, "Instruction", `In Direct Message, use \`${prefix}new Your thread title here\` to create a new thread (**Thread Created!** will be displayed). You don't need to use any command after your thread created. Describe your issue afterward.`); + break; + } + } + return replyChannel.send(helpEmbed); } const name = args[0].toLowerCase(); const command = commands.get(name) || commands.find(cmd => cmd.aliases && cmd.aliases.includes(name)); if(!command) { - return message.channel.send(notCmdEmbed); + console.log("> Not a command name or alias."); + return replyChannel.send(notCmdEmbed); } data.push(`**Name** : ${command.name}`); - if (command.aliases) data.push(`**Aliases** : ${command.aliases.join(', ')}`); + if (command.aliases) data.push(`**Aliases** : ${command.aliases.join(", ")}`); if (command.level) data.push(`**Required Level** : ${command.level}`); if(command.guildOnly) { - data.push(`**Direct Message** : false`); - } else { - data.push(`**Direct Message** : true`); + data.push("**Direct Message** : false"); + } + else { + data.push("**Direct Message** : true"); + } + const usages = []; + if(!command.args) usages.push(`\`${prefix}${command.name}\``); + if(command.usage) { + command.usage.forEach(key => { + usages.push(`\`${prefix}${command.name} ${key}\``); + }); } - if (command.usage) data.push(`**Usage** : \`${prefix}${command.name} ${command.usage}\``); + data.push(`**Usage** : ${usages.join(", ")}`); if (command.description) data.push(`**Description** : ${command.description}`); if (command.note) data.push(`**Note** : \`${command.note}\``); - const dataEmbed = getEmbed.execute(param, config.info_color, "Command Info", data.join('\n')); - return message.channel.send(dataEmbed); - } + const dataEmbed = getEmbed.execute(param, "", config.info_color, "Command Info", data.join("\n")); + return replyChannel.send(dataEmbed); + }, }; diff --git a/commands/helpchs.js b/commands/helpchs.js deleted file mode 100644 index 55ef28e..0000000 --- a/commands/helpchs.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpchs', - aliases: ['助攻'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: '显示如何使用操作这个系统的说明.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "指令", `在“直接消息”中,使用\`${config.prefix}new 您的线程标题在这里\`创建一个新线程(显示“**Thread Created!**”)。创建线程后,无需使用任何命令。然后描述你的问题。`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helpcht.js b/commands/helpcht.js deleted file mode 100644 index af0a9d9..0000000 --- a/commands/helpcht.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpcht', - aliases: ['助攻'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: '顯示如何使用操作這個系統的說明.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "指令", `在“直接消息”中,使用\`${config.prefix}new 您的線程標題在這裡\`創建一個新線程(顯示“**Thread Created!**”)。創建線程後,無需使用任何命令。然後描述你的問題。`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helpde.js b/commands/helpde.js deleted file mode 100644 index c668055..0000000 --- a/commands/helpde.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpde', - aliases: ['hilfe'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: 'Anleitung zur Verwendung des Bots anzeigen.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "Anweisung", `In Direktnachrichten, Verwenden Sie \`${config.prefix}new Dein Threadtitel hier\`, um einen neuen Thread zu erstellen (es wird **Thread Created!** Angezeigt). Sie müssen nach dem Erstellen Ihres Threads keinen Befehl mehr verwenden. Beschreiben Sie anschließend Ihr Problem.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helpes.js b/commands/helpes.js deleted file mode 100644 index e8cf4c4..0000000 --- a/commands/helpes.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpes', - aliases: ['ayuda'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: 'Mostrar instrucciones sobre cómo usar el bot.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "Instrucción", `En un mensaje privado, utilice \`${config.prefix}new el tema de su problema\` para crear un nuevo ticket (se muestra **Thread Created!**). No necesita usar ningún comando después de la creación del ticket. Luego describe su problema.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helpfr.js b/commands/helpfr.js deleted file mode 100644 index ffabfad..0000000 --- a/commands/helpfr.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpfr', - aliases: ['aide'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: 'Afficher les instructions sur l\'utilisation du bot.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "Instruction", `En message privé, utilisez \`${config.prefix}new le sujet de votre problème\` pour créer un nouveau ticket (**Thread Created!** s'affiche). Vous n'avez pas besoin d'utiliser de commande après la création du ticket. Décrivez ensuite votre problème.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helpkr.js b/commands/helpkr.js deleted file mode 100644 index 0aca594..0000000 --- a/commands/helpkr.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpkr', - aliases: ['도움'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: '봇 사용 방법에 대한 지시 사항 표시.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "훈령", `직접 메시지에서\`${config.prefix}new 스레드 제목은 여기\`를 사용하여 새 스레드를 만듭니다 (**Thread Created!**가 표시됨). 스레드가 작성된 후 명령을 사용할 필요가 없습니다. 그런 다음 문제를 설명하십시오.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helppt.js b/commands/helppt.js deleted file mode 100644 index d6b2e39..0000000 --- a/commands/helppt.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helppt', - aliases: ['ajuda'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: 'Mostrar instruções sobre como usar o bot.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "Instrução", `Na mensagem direta, usar \`${config.prefix}new Título do assunto\` para criar um novo tópico (**Thread Created!** é exibido). Não precisa de usar nenhum comando depois de criar o seu tópico. Em seguida, descreva o seu problema.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helpru.js b/commands/helpru.js deleted file mode 100644 index 6da9143..0000000 --- a/commands/helpru.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helpru', - aliases: ['помощь'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: 'Показать инструкцию о том, как использовать бот.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "инструкция", `В прямом сообщении используйте \`${config.prefix}new Ваше название темы здесь\`, чтобы создать новую тему (**Thread Created!** Отображается). Вам не нужно использовать какую-либо команду после создания вашего потока. Тогда опишите свою проблему.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/helptr.js b/commands/helptr.js deleted file mode 100644 index e41afd1..0000000 --- a/commands/helptr.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - name: 'helptr', - aliases: ['yardım'], - level: 'User', - guildOnly: false, - args: false, - usage: false, - description: 'Bot kullanımı hakkında talimat göster.', - note: false, - async execute(param, message, args) { - const config = param.config; - const prefix = config.prefix; - const getEmbed = param.getEmbed; - - const helpEmbed = getEmbed.execute(param, config.info_color, "Talimat", `Doğrudan Mesaj'da, yeni bir bilet oluşturmak için \`${config.prefix}new konuyu buraya yazın\` yazın ve gönderin (**Thread Created!** görüntülenir). Konuyu oluşturduktan sonra herhangi bir komut kullanmanıza gerek yoktur. Daha sonra sorununuzu açıklayın.`); - - return message.channel.send(helpEmbed); - - } -}; diff --git a/commands/leave.js b/commands/leave.js index efc679c..9fb6858 100644 --- a/commands/leave.js +++ b/commands/leave.js @@ -1,36 +1,33 @@ module.exports = { - name: 'leave', + name: "leave", aliases: false, - level: 'Owner', + level: "Owner", guildOnly: false, args: true, - usage: '', - description: 'Leave a guild (server).', + reqConfig: false, // Configs needed to run this command. + usage: [""], + description: "Leave a guild (server).", note: false, - async execute(param, message, args) { + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const config = param.config; const getEmbed = param.getEmbed; const serverID = args.shift(); - const getServer = client.guilds.cache.get(serverID); + const getServer = await client.guilds.fetch(serverID); - const notFoundEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Can't find guild with ID (\`${serverID}\`) in my collection.`); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); + const notFoundEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `Can't find guild with ID (\`${serverID}\`) in my collection.`); - if (message.author.id === config.botOwnerID) { - // bot owner - if(getServer) { - const successEmbed = getEmbed.execute(param, config.info_color, "Leaving", `Leaving [**${getServer.name}**] (\`${getServer.id}\`) guild.`); - console.log(`Leaving [${getServer.name}] guild.`); - return getServer.leave().then(message.channel.send(successEmbed)); - } else { - message.channel.send(notFoundEmbed); - } - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - return; - } else { - return message.channel.send(noPermEmbed); + if(getServer) { + const successEmbed = getEmbed.execute(param, "", config.info_color, "Leaving", `Leaving [**${getServer.name}**] (\`${getServer.id}\`) guild.`); + console.log(`> Leaving [${getServer.name}] guild.`); + return getServer.leave().then(replyChannel.send(successEmbed)); + } + else { + console.log(`> Can't fetch ${serverID} guild.`); + return replyChannel.send(notFoundEmbed); } - } + }, }; diff --git a/commands/new.js b/commands/new.js index a285258..fb57e60 100644 --- a/commands/new.js +++ b/commands/new.js @@ -1,19 +1,22 @@ module.exports = { - name: 'new', - aliases: ['neu', 'yeni', '새로운', 'novo', 'nouveau', 'новый', '新', 'nuevo'], - level: 'User', + name: "new", + aliases: ["neu", "yeni", "새로운", "novo", "nouveau", "новый", "新", "nuevo"], + level: "User", guildOnly: false, args: true, - usage: '[thread title]', - description: 'Create new thread.', + reqConfig: ["mainServerID", "threadServerID", "categoryID", "logChannelID"], // Configs needed to run this command. + usage: ["[thread title]"], + description: "Create new thread.", note: false, - async execute(param, message, args) { + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const db = param.db; const threadPrefix = param.dbPrefix.thread; const config = param.config; const getEmbed = param.getEmbed; - const newThread = param.new; + const newThread = param.newThread; const isMember = param.isMember; const isBlocked = param.isBlocked; @@ -21,63 +24,49 @@ module.exports = { const mainServer = await client.guilds.cache.get(mainServerID); const threadServerID = config.threadServerID; const threadServer = await client.guilds.cache.get(threadServerID); - const botChannelID = config.botChannelID; const categoryID = config.categoryID; const categoryChannel = await threadServer.channels.cache.get(categoryID); - const logChannelID = config.logChannelID; const author = message.author; const isThread = await db.get(threadPrefix + author.id); const checkIsMember = await isMember.execute(param, author.id); const checkIsBlocked = await isBlocked.execute(param, author.id); - const notMemberEmbed = getEmbed.execute(param, config.error_color, "Not a Member", `You aren't inside [**${mainServer.name}**] guild.`); - const blockedEmbed = getEmbed.execute(param, config.error_color, "Blocked", `You are blocked from creating new thread.`); - const isThreadEmbed = getEmbed.execute(param, config.error_color, "Thread Detected", `You still have open thread.`); - const noCategoryEmbed = getEmbed.execute(param, config.error_color, "Error", "Please contact server admin.\n**Error** : `Couldn't find ModMail category channel.`"); - const noServerEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`categoryID` and/or `logChannelID` value is empty."); - const notDMEmbed = getEmbed.execute(param, config.error_color, "Command Unavailable", "This command can only be used in Direct Message.") - const maxEmbed = getEmbed.execute(param, config.error_color, "Maximum Thread Reached", `Maximum threads for this server is reached, please wait until some of the threads closed.`) + const notMemberEmbed = getEmbed.execute(param, "", config.error_color, "Not a Member", `You aren't inside [**${mainServer.name}**] guild.`); + const blockedEmbed = getEmbed.execute(param, "", config.error_color, "Blocked", "You are blocked from creating new thread."); + const isThreadEmbed = getEmbed.execute(param, "", config.error_color, "Thread Detected", "You still have open thread."); + const notDMEmbed = getEmbed.execute(param, "", config.error_color, "Command Unavailable", "This command can only be used in Direct Message."); + const maxEmbed = getEmbed.execute(param, "", config.error_color, "Maximum Thread Reached", "Maximum threads for this server is reached, please wait until some of the threads closed."); - if (message.guild != null && (message.guild.id == mainServerID || message.guild.id == threadServerID)) { - // Inside a main server or thread server - if(botChannelID != "empty" && message.channel.id != botChannelID) { - // Outside bot channel if it isn't empty - return; - } else { - // Inside bot channel if it isn't empty or any channel if it's empty - return message.channel.send(notDMEmbed); - } - } else if(message.guild != null) { + if(message.guild != null) { // Outside main server or thread server not Direct Message - return message.channel.send(notDMEmbed); - } else if(mainServerID == "empty" || threadServerID == "empty") { - // Direct Message - // Value of mainServerID or threadServerID is empty - return message.channel.send(noServerEmbed); - } else if (categoryID == "empty" || logChannelID == "empty") { - // Value of categoryID or logChannelID is empty - return message.channel.send(noChannelEmbed); - } else if (isThread) { + console.log("> Not a Direct Message channel."); + return replyChannel.send(notDMEmbed); + } + else if (isThread) { // There's still open thread - return message.channel.send(isThreadEmbed); - } else if (!checkIsMember) { + console.log("> User still have an open thread."); + return replyChannel.send(isThreadEmbed); + } + else if (!checkIsMember) { // User isn't a member - return message.channel.send(notMemberEmbed); - } else if (checkIsBlocked) { + console.log("> User aren't a member of main server."); + return replyChannel.send(notMemberEmbed); + } + else if (checkIsBlocked) { // User is blocked - return message.channel.send(blockedEmbed); - } else if (!categoryChannel) { - // Can't find category - return message.channel.send(noCategoryEmbed); - } else if (categoryChannel.children.size == 50) { + console.log("> User are blocked."); + return replyChannel.send(blockedEmbed); + } + else if (categoryChannel.children.size == 50) { // Maximum children for a category reached - return message.channel.send(maxEmbed); - } else { + console.log("> Category reached maximum channels."); + return replyChannel.send(maxEmbed); + } + else { // none of above, calling newThread function return newThread.execute(param, message, args); } - } + }, }; diff --git a/commands/ping.js b/commands/ping.js index 99fbc5f..52e9b11 100644 --- a/commands/ping.js +++ b/commands/ping.js @@ -1,49 +1,24 @@ module.exports = { - name: 'ping', + name: "ping", aliases: false, - level: 'User', + level: "User", guildOnly: false, args: false, + reqConfig: false, // Configs needed to run this command. usage: false, - description: 'Calculate bot latency.', + description: "Calculate bot latency.", note: false, - async execute(param, message, args) { + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const config = param.config; const getEmbed = param.getEmbed; - if(message.guild != null) { - if(config.mainServerID != "empty" && config.threadServerID != "empty") { - // mainServerID and threadServerID isn't empty - if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID != "empty") { - // adminRoleID isn't empty - if(config.modRoleID != "empty") { - // modRoleID isn't empty - if(!message.member.hasPermission("ADMINISTRATOR") && !await param.roleCheck.execute(message, config.adminRoleID) && !await param.roleCheck.execute(message, config.modRoleID)) { - // user don't have ADMINISTRATOR permission, admin role, nor a mod role - if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // not bot channel - return; - } - } - } else if(!message.member.hasPermission("ADMINISTRATOR") && !await param.roleCheck.execute(message, config.adminRoleID)) { - // user don't have ADMINISTRATOR permission nor an admin role - if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // not bot channel - return; - } - } - } - } - } - } - - const pingEmbed = getEmbed.execute(param, config.info_color, "Pong", "Ping?"); + const pingEmbed = getEmbed.execute(param, "", config.info_color, "Pong", "Ping?"); - message.channel.send(pingEmbed).then((msg) => { - const editEmbed = getEmbed.execute(param, config.info_color, "Pong", `**Response time** : **${msg.createdTimestamp - message.createdTimestamp}** ms\n**API latency** : **${Math.round(param.client.ws.ping)}** ms`); + replyChannel.send(pingEmbed).then((msg) => { + const editEmbed = getEmbed.execute(param, "", config.info_color, "Pong", `**Response time** : **${msg.createdTimestamp - message.createdTimestamp}** ms\n**API latency** : **${Math.round(param.client.ws.ping)}** ms`); msg.edit(editEmbed); }); - } + }, }; diff --git a/commands/reload.js b/commands/reload.js index e2946cd..6c09c7c 100644 --- a/commands/reload.js +++ b/commands/reload.js @@ -1,52 +1,57 @@ module.exports = { - name: 'reload', + name: "reload", aliases: false, - level: 'Owner', + level: "Owner", guildOnly: false, args: true, - usage: '[command name]', - description: 'Reload a command.', - note: false, - async execute(param, message, args) { + reqConfig: false, // Configs needed to run this command. + usage: ["", ""], + description: "Reloading command or function.", + note: "Used to apply changes made to the bot on commands and functions file without restarting the bot.", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const config = param.config; const getEmbed = param.getEmbed; - const commandName = args.shift().toLowerCase(); + const fnName = args.shift(); // Function name are case sensitive + const commandName = fnName.toLowerCase(); // Command name case are case insensitive. const command = client.commands.get(commandName) || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName)); - const fnName = commandName + "Fn"; const fn = client.functions.get(fnName); + const cmdMap = client.commands.map(cmd => `\`${cmd.name}\``).join(", "); + const fnMap = client.functions.map(fnc => `\`${fnc.name}\``).join(", "); + const cmdField = `Command Names:;${cmdMap}`; + const fnField = `Function Names:;${fnMap}`; - const notCmdEmbed = getEmbed.execute(param, config.error_color, "Not a Command", `That's not a valid command name or alias.\nUse \`${config.prefix}commands\` to show available commands.`); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); + const notCmdEmbed = getEmbed.execute(param, "", config.error_color, "Invalid Arguments", "That's not a valid command or function name.", [cmdField, fnField]); + const data = []; - if (message.author.id === config.botOwnerID) { - // bot owner - if(!command) { - return message.channel.send(notCmdEmbed); - } - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Command \`${command.name}\` was reloaded.`); - console.log(`Deleting ${command.name} cache.`); + if(command) { + console.log(`> Deleting ${command.name} cache.`); delete require.cache[require.resolve(`./${command.name}.js`)]; - console.log(`Loading ${command.name}.`) + console.log(`> Loading ${command.name}.`); const newCommand = require(`./${command.name}.js`); client.commands.set(newCommand.name, newCommand); + data.push(`Reloaded \`${command.name}\` command.`); + } + if(fn) { + console.log(`> Deleting ${fnName} cache.`); + delete require.cache[require.resolve(`../functions/${fnName}.js`)]; - if(fn) { - console.log(`Deleting ${fnName} cache.`); - delete require.cache[require.resolve(`../functions/${fnName}.js`)]; - - console.log(`Loading ${fnName}.`) - const newFunction = require(`../functions/${fnName}.js`); - client.functions.set(newFunction.name, newFunction); - } - - return message.channel.send(successEmbed); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - return; - } else { - return message.channel.send(noPermEmbed); + console.log(`> Loading ${fnName}.`); + const newFunction = require(`../functions/${fnName}.js`); + client.functions.set(newFunction.name, newFunction); + param[fnName] = client.functions.get(fnName); + data.push(`Reloaded \`${fnName}\` function.`); + } + if(!command && !fn) { + console.log("> Invalid Name."); + return replyChannel.send(notCmdEmbed); } - } + + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", data.join("\n")); + return replyChannel.send(successEmbed); + }, }; diff --git a/commands/reply.js b/commands/reply.js index f747a78..625d4eb 100644 --- a/commands/reply.js +++ b/commands/reply.js @@ -1,62 +1,88 @@ module.exports = { - name: 'reply', + name: "reply", aliases: false, - level: 'Moderator', + level: "Moderator", guildOnly: true, - // in case only attachment no message - args: false, - usage: '[reply message]', - description: 'Reply to a user thread.', + args: false, // in case there's only attachment with no message + reqConfig: ["mainServerID"], // Configs needed to run this command. + usage: ["[reply message]"], + description: "Reply to a user thread.", note: false, - async execute(param, message, args) { - const config = param.config; + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const MessageAttachment = param.MessageAttachment; + const client = param.client; const getEmbed = param.getEmbed; - const reply = param.reply; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Configuration Needed", "`categoryID` and/or `logChannelID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - const notChannelEmbed = getEmbed.execute(param, config.error_color, "Invalid Channel", `This isn't thread channel.`); - const noArgsEmbed = param.getEmbed.execute(param, config.warning_color, "Missing Arguments", `You didn't provide any arguments nor attachments.`); - - // Yes i know it's nasty to look at that many nested if else but it's needed @,@ - if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - return message.channel.send(noServerEmbed); - } else if(message.guild.id == config.threadServerID) { - // inside thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID or modRoleID empty - return message.channel.send(noAdminEmbed); - } else if(config.categoryID == "empty" || config.logChannelID == "empty") { - // categoryID or logChannelID empty - return message.channel.send(noChannelEmbed); - } else if (message.channel.parentID != config.categoryID || message.channel.id == config.logChannelID || message.channel.id == config.botChannelID) { - // adminRoleID, modRoleID, categoryID and logChannelID not empty - // the channel isn't under modmail category or it's a log channel or it's bot channel -_- - return message.channel.send(notChannelEmbed); - } else if(!args.length && message.attachments.size == 0) { - // the channel is under modmail category, it's not a log channel, and it's not bot channel - return message.channel.send(noArgsEmbed); - } else if(message.author.id == config.botOwnerID) { - return reply.execute(param, message, args); - } else if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return reply.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return reply.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); + const config = param.config; + const db = param.db; + const threadPrefix = param.dbPrefix.thread; + const isMember = param.isMember; + const isBlocked = param.isBlocked; + + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.fetch(mainServerID); + const author = message.author; + + const userID = message.channel.name.split("-").pop(); + const isThread = await db.get(threadPrefix + userID); + const checkIsBlocked = await isBlocked.execute(param, author.id); + + const blockedEmbed = getEmbed.execute(param, "", config.error_color, "Blocked", "User blocked."); + const noDMEmbed = getEmbed.execute(param, "", config.error_color, "Not Sent", "User disabled Direct Message."); + const noUserEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find user in my collection."); + const noThreadEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find any thread asociated with this channel."); + + if (!isThread) { + console.log("> Thread not found."); + return replyChannel.send(noThreadEmbed); + } + else { + const checkIsMember = await isMember.execute(param, userID); + const notMemberEmbed = getEmbed.execute(param, "", config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); + + if (!checkIsMember) { + console.log("> The user isn't a main server member."); + return replyChannel.send(notMemberEmbed); + } + else if (checkIsBlocked) { + console.log("> The user is blocked."); + return replyChannel.send(blockedEmbed); } + else { + const member = await mainServer.members.fetch(userID); - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + if (!member) { + console.log("> Can't fetch user data."); + return replyChannel.send(noUserEmbed); + } + else { + const user = member.user; + const description = args.join(" "); + const userDMEmbed = getEmbed.execute(param, author, config.sent_color, "Message Received", description, "", mainServer); + const threadChannelEmbed = getEmbed.execute(param, author, config.sent_color, "Message Sent", description, "", user); + + try{ + await user.send(userDMEmbed); + } + catch (error) { + if(error.message == "Cannot send messages to this user") { + console.log("> Recipient's DM are disabled."); + return replyChannel.send(noDMEmbed); + } + } + await replyChannel.send(threadChannelEmbed); + if (message.attachments.size > 0) { + await message.attachments.forEach(async atch => { + const attachment = new MessageAttachment(atch.url); + await user.send(attachment); + await replyChannel.send(attachment); + }); + } + return message.delete().then(() => console.log("> Message deleted.")); + } + + } } - } + }, }; diff --git a/commands/reset.js b/commands/reset.js index c46cafd..39b0782 100644 --- a/commands/reset.js +++ b/commands/reset.js @@ -1,51 +1,47 @@ module.exports = { - name: 'reset', + name: "reset", aliases: false, - level: 'Admin', + level: "Admin", guildOnly: true, args: false, - usage: false, - description: 'Reset all configuration values.', + reqConfig: false, // Configs needed to run this command. + usage: ["all", ""], + description: "Reset specific or all configuration values.", note: false, - async execute(param, message, args) { + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const config = param.config; const getEmbed = param.getEmbed; - const reset = param.reset; + const defConfig = param.defConfig; + const db = param.db; + const configPrefix = param.dbPrefix.config; + const configKeys = Object.keys(param.config); + + const firstArg = args.shift(); + let resetName; + if(!firstArg || firstArg == "all") resetName = "All"; + else resetName = `\`${firstArg}\``; - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `All configuration value have been reset to default.`); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` value is empty."); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `${resetName} config value is reset to default value.`); - if (message.author.id === config.botOwnerID) { - // bot owner - console.log("Resetting bot configuration..."); - return reset.execute(param).then(message.channel.send(successEmbed)); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - // reset and restart the bot - console.log("Resetting bot configuration..."); - return reset.execute(param).then(message.channel.send(successEmbed)); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - console.log("Resetting bot configuration..."); - return reset.execute(param).then(message.channel.send(successEmbed)); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + if(!firstArg || firstArg == "all") { + configKeys.forEach(async aKey => { + const dbKey = configPrefix + aKey; + console.log(`> Resetting ${dbKey} value to ${defConfig[aKey]}.`); + await db.set(dbKey, defConfig[aKey]); + }); + } + else if(configKeys.includes(firstArg)) { + const dbKey = configPrefix + firstArg; + console.log(`> Resetting ${dbKey} value to ${defConfig[firstArg]}.`); + await db.set(dbKey, defConfig[firstArg]); + } + else { + const invalidEmbed = getEmbed.execute(param, "", config.warning_color, "Not a Config Name", `That's not a valid config name.\nUse \`${config.prefix}config\` to show available configs.`); + console.log("> Not a config name."); + return replyChannel.send(invalidEmbed); } - } + return param.configSync.execute(param).then(() => replyChannel.send(successEmbed)); + }, }; diff --git a/commands/set.js b/commands/set.js index a23af94..f3db835 100644 --- a/commands/set.js +++ b/commands/set.js @@ -1,46 +1,215 @@ module.exports = { - name: 'set', + name: "set", aliases: false, - level: 'Admin', + level: "Admin", guildOnly: true, args: true, - usage: '[config name] [value]', - description: 'Set specific configuration value.', - note: 'Config name case is sensitive (upper case and lower case are different).', - async execute(param, message, args) { + reqConfig: false, // Configs needed to run this command. + usage: [" "], + description: "Set specific configuration value.", + note: "Config name case is sensitive (upper case and lower case are different).", + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; const config = param.config; const getEmbed = param.getEmbed; - const set = param.set; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return await set.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return await set.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return await set.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } + const configSync = param.configSync; + const db = param.db; + const configPrefix = param.dbPrefix.config; + const configName = args.shift(); + const dbKey = configPrefix + configName; + let inputValue = args.shift() || "empty"; + + // manual toggle since database can only stor String, can't store boolean for maintenance config. + if(configName == "maintenance") { + if(config.maintenance == "0") { + inputValue = "1"; + } + else { + inputValue = "0"; + } + } + console.log(`> Input name: ${configName}, value: ${inputValue}.`); + + const notOwnerEmbed = getEmbed.execute(param, "", config.warning_color, "Missing Permission", `Only bot owner [<@${config.botOwnerID}>] can change this value.`); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `The value of \`${configName}\` changed to \`${inputValue}\``); + const notSetEmbed = getEmbed.execute(param, "", config.warning_color, "Configuration Needed", "Please set `mainServerID` and `threadServerID` to change this config."); + const emptyValueEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Value", "This config value can't be empty."); + const invalidUserEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid User", "Can't find that user."); + const notNumberEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Argument", "That isn't a number."); + const negativeNumberEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Argument", "The value can't be negative."); + const invalidServerEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Server", "Can't find that server.\n`Make sure the bot joined the server already`"); + const invalidChannelMainEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Channel", "Can't find that channel.\n`Make sure the channel is inside Main Server.`"); + const invalidChannelThreadEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Channel", "Can't find that channel.\n`Make sure the channel is inside Thread Server.`"); + const invalidCategoryEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Category", "That isn't a category channel."); + const invalidTextChannelEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Channel", "That isn't a text channel."); + const invalidRoleEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Role", "Can't find that role.\n`Make sure the role is inside Thread Server.`"); + const invalidColorEmbed = getEmbed.execute(param, "", config.warning_color, "Invalid Color", "Use hex code for input.\nCheck : "); + + // elimination for invalid input + if(configName == "botOwnerID") { + + if(!param.isOwner) { + console.log("> Not bot owner."); + return replyChannel.send(notOwnerEmbed); + } + else if(inputValue == "empty") { + console.log("> Value can't be empty."); + return replyChannel.send(emptyValueEmbed); + } + else if(!client.users.cache.get(inputValue)) { + console.log("> User not found."); + return replyChannel.send(invalidUserEmbed); + } + + } + else if(configName == "cooldown") { + + if (isNaN(inputValue)) { + console.log("> Input value isn't a number"); + return replyChannel.send(notNumberEmbed); + } + else if(inputValue < 0) { + console.log("> Negative input value."); + return replyChannel.send(negativeNumberEmbed); + } + + } + else if(configName == "mainServerID" && inputValue != "empty") { + + if(!client.guilds.cache.get(inputValue)) { + console.log("> Server not found."); + return replyChannel.send(invalidServerEmbed); + } + + } + else if(configName == "threadServerID" && inputValue != "empty") { + + if(!client.guilds.cache.get(inputValue)) { + console.log("> Server not found."); + return replyChannel.send(invalidServerEmbed); + } + + } + else if(configName == "categoryID" && inputValue != "empty") { + + const threadServer = await client.guilds.fetch(config.threadServerID); + if(!threadServer) { + console.log("> Invalid threadServerID."); + return replyChannel.send(notSetEmbed); + } + + const getChannel = client.guilds.cache.get(config.threadServerID).channels.cache.get(inputValue); + if(!getChannel) { + console.log("> Channel not found."); + return replyChannel.send(invalidChannelThreadEmbed); + } + else if(getChannel.type != "category") { + console.log("> Not a category channel."); + return replyChannel.send(invalidCategoryEmbed); + } + + } + else if(configName == "logChannelID" && inputValue != "empty") { + + const threadServer = await client.guilds.fetch(config.threadServerID); + if(!threadServer) { + console.log("> Invalid threadServerID."); + return replyChannel.send(notSetEmbed); + } + + const getChannel = client.guilds.cache.get(config.threadServerID).channels.cache.get(inputValue); + if(!getChannel) { + console.log("> Channel not found."); + return replyChannel.send(invalidChannelThreadEmbed); + } + else if(getChannel.type != "text") { + console.log("> Not a text channel."); + return replyChannel.send(invalidTextChannelEmbed); + } + + } + else if(configName == "botChannelID" && inputValue != "empty") { + + const mainServer = await client.guilds.fetch(config.mainServerID); + if(!mainServer) { + console.log("> Invalid mainServerID"); + return replyChannel.send(notSetEmbed); + } + + const getChannel = client.guilds.cache.get(config.mainServerID).channels.cache.get(inputValue); + if(!getChannel) { + console.log("> Channel not found."); + return replyChannel.send(invalidChannelMainEmbed); + } + else if(getChannel.type != "text") { + console.log("> Not a text channel."); + return replyChannel.send(invalidTextChannelEmbed); + } + + } + else if((configName == "adminRoleID" || configName == "modRoleID") && inputValue != "empty") { + + const threadServer = await client.guilds.fetch(config.threadServerID); + if(!threadServer) { + console.log("> Invalid threadServerID."); + return replyChannel.send(notSetEmbed); + } + else if(!client.guilds.cache.get(config.threadServerID).roles.cache.get(inputValue)) { + console.log("> Role not found."); + return replyChannel.send(invalidRoleEmbed); + } + + } + else if(configName == "mentionedRoleID" && inputValue != "empty" && inputValue != "everyone" && inputValue != "here") { + + const threadServer = await client.guilds.fetch(config.threadServerID); + if(!threadServer) { + console.log("> Invalid threadServerID."); + return replyChannel.send(notSetEmbed); + } + else if(!client.guilds.cache.get(config.threadServerID).roles.cache.get(inputValue)) { + console.log("> Role not found."); + return replyChannel.send(invalidRoleEmbed); + } + + } + else if(configName == "info_color" || configName == "warning_color" || configName == "error_color" || configName == "sent_color" || configName == "received_color") { + + const colorTest = /^#[0-9A-F]{6}$/i; + if(inputValue == "empty") { + console.log("> Empty value."); + return replyChannel.send(emptyValueEmbed); + } + else if(colorTest.test(inputValue) == false) { + console.log("> Invalid input."); + return replyChannel.send(invalidColorEmbed); + } + + } + + // getting all the config name from Database + const configCollection = await db.list(configPrefix); + const configList = configCollection.map(conf => `\`${conf.slice(configPrefix.length)}\``).join(", "); + + if(configCollection.includes(dbKey)) { + await db.set(dbKey, inputValue); + console.log(`> [${dbKey}] value changed to [${inputValue}]`); + await replyChannel.send(successEmbed).then(async () => { + await configSync.execute(param); + if(configName == "maintenance" || configName == "prefix") { + setTimeout(async ()=> { + await param.updateActivity.execute(param); + }, 5000); + } + }); + } + else { + const notFoundEmbed = getEmbed.execute(param, "", config.error_color, "Invalid Arguments", `Can't find config named \`${configName}\`.\nAvailable names : ${configList}`); + console.log("> Invalid config name."); + return replyChannel.send(notFoundEmbed); + } + + }, }; diff --git a/commands/tag.js b/commands/tag.js index 0a7091c..e81c333 100644 --- a/commands/tag.js +++ b/commands/tag.js @@ -1,49 +1,254 @@ module.exports = { - name: 'tag', - aliases: ['t'], - level: 'Moderator', + name: "tag", + aliases: ["t"], + level: "Moderator", guildOnly: true, args: true, - usage: '[tag name]', - description: 'Send a saved response.', + reqConfig: ["mainServerID"], // Configs needed to run this command. + usage: ["", "", " ", " ", " ", " "], + description: "Send, add, delete, edit, show an info or show list of saved response(s).", note: false, - async execute(param, message, args) { + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; const config = param.config; + const db = param.db; + const tagPrefix = param.dbPrefix.tag; + const threadPrefix = param.dbPrefix.thread; const getEmbed = param.getEmbed; - const tag = param.tag; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return tag.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return tag.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return tag.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return tag.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + const author = message.author; + const channel = message.channel; + + const tagCollection = await db.list(tagPrefix); + const tagList = tagCollection.map(tag => `\`${tag.slice(tagPrefix.length)}\``).join(", ") || "No available tag"; + + const cancelEmbed = getEmbed.execute(param, "", config.error_color, "Canceled", "Command are canceled."); + const timeoutEmbed = getEmbed.execute(param, "", config.error_color, "Timeout", "Timeout, command are canceled."); + + const firstArg = args.shift(); + switch(firstArg.toLowerCase()) { + case "l": // fallthrough + case "list": { + const tagListEmbed = getEmbed.execute(param, "", config.info_color, "Tags", tagList); + replyChannel.send(tagListEmbed); + break; + } + case "+": // fallthrough + case "add": { + const tagName = args.join(" ").toLowerCase(); + + const duplicatedEmbed = getEmbed.execute(param, "", config.error_color, "Duplicated", `There's a tag named (\`${tagName}\`) already.`); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `Succesfully add (\`${tagName}\`) tag.`); + const waitingEmbed = getEmbed.execute(param, "", config.info_color, "Response", "Please write the response for this tag.\nType `cancel` to cancel the command.\n\n`Timeout: 30 seconds.`"); + + const dbKey = tagPrefix + tagName; + const isDuplicated = await db.get(dbKey); + if(isDuplicated) { + console.log("> Duplicated tag name."); + return replyChannel.send(duplicatedEmbed); + } + else { + const filter = msg => msg.author.id == message.author.id; + + replyChannel.send(waitingEmbed).then(() => { + message.channel.awaitMessages(filter, { max: 1, time: 30000, errors: ["time"] }) + .then(async collected => { + if (collected.first().content.toLowerCase() == "cancel") { + console.log("> Command canceled."); + return replyChannel.send(cancelEmbed); + } + else { + const content = collected.first().content; + db.set(dbKey, content).then(() => { + console.log(`> Added [${tagName}] tag.`); + return replyChannel.send(successEmbed); + }); + } + }) + .catch(async () => { + console.log("> Timeout."); + return replyChannel.send(timeoutEmbed); + }); + }); + } + break; + } + case "-": // fallthrough + case "del": { + const tagName = args.join(" ").toLowerCase(); + const dbKey = tagPrefix + tagName; + const isTag = await db.get(dbKey); + + const noTagEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `Couldn't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `Deleted (\`${tagName}\`) tag.`); + + if(!isTag) { + console.log("> Tag not found."); + return replyChannel.send(noTagEmbed); + } + else { + db.delete(dbKey).then(() => { + console.log(`> Deleted ${tagName}.`); + return replyChannel.send(successEmbed); + }); + } + break; + } + case "=": // fallthrough + case "edit": { + const tagName = args.join(" ").toLowerCase(); + const dbKey = tagPrefix + tagName; + const isTag = await db.get(dbKey); + + const noTagEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `Couldn't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `Succesfully edit (\`${tagName}\`) tag response.`); + const waitingEmbed = getEmbed.execute(param, "", config.info_color, "Response", "Please write new response for this tag.\nType `cancel` to cancel the command.\n\n`Timeout: 30 seconds.`"); + + if(!isTag) { + console.log("> Tag not found."); + return replyChannel.send(noTagEmbed); + } + else { + const filter = msg => msg.author.id == message.author.id; + + replyChannel.send(waitingEmbed).then(() => { + message.channel.awaitMessages(filter, { max: 1, time: 30000, errors: ["time"] }) + .then(async collected => { + if (collected.first().content.toLowerCase() == "cancel") { + return replyChannel.send(cancelEmbed); + } + else { + const content = collected.first().content; + db.set(dbKey, content).then(() => { + console.log(`> Edited [${tagName}] tag.`); + return replyChannel.send(successEmbed); + }); + } + }) + .catch(async () => { + console.log("> Timeout."); + return replyChannel.send(timeoutEmbed); + }); + }); + } + break; + } + case "?": // fallthrough + case "info": { + const tagName = args.join(" ").toLowerCase(); + const dbKey = tagPrefix + tagName; + const isTag = await db.get(dbKey); + + const noTagEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `CouldnS't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); + + if(!isTag) { + console.log("> Tag not found."); + return replyChannel.send(noTagEmbed); + } + else { + const data = []; + data.push(`**Name** : ${tagName}`); + data.push(`**Response** : \`\`\`${isTag}\`\`\``); + + const tagInfoEmbed = getEmbed.execute(param, "", config.info_color, "Tag Information", data.join("\n")); + replyChannel.send(tagInfoEmbed); + } + break; + } + default: { + args.unshift(firstArg); + const tagName = args.join(" ").toLowerCase(); + + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.cache.get(mainServerID); + const dbKey = tagPrefix + tagName; + + const isTag = await db.get(dbKey); + const userID = channel.name.split("-").pop(); + const isThread = await db.get(threadPrefix + userID); + + const noTagEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `Couldn't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); + const noDMEmbed = getEmbed.execute(param, "", config.error_color, "Not Sent", "User disabled Direct Message."); + + if(!isTag) { + // can't find tag + console.log("> Tag not found."); + return replyChannel.send(noTagEmbed); + } + else if(!isThread) { + // no user thread + console.log("> Not a thread channel."); + const noThreadEmbed = getEmbed.execute(param, "", config.info_color, "", isTag); + return message.channel.send(noThreadEmbed).then(message.delete()); + } + else { + // There's user thread and tag + console.log("> Thread channel."); + const checkIsBlocked = await param.isBlocked.execute(param, userID); + const checkIsMember = await param.isMember.execute(param, author.id); + const blockedEmbed = getEmbed.execute(param, "", config.error_color, "Blocked", "User blocked."); + const notMemberEmbed = getEmbed.execute(param, "", config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); + + const filter = (reaction, user) => { + return user.id === message.author.id && (reaction.emoji.name == "✅" || reaction.emoji.name == "❌"); + }; + + const waitingEmbed = getEmbed.execute(param, "", config.info_color, isTag + "\n\nReact with ✅ to send, ❌ to cancel.\n`Timeout: 30 seconds.`"); + + const botMsg = await replyChannel.send(waitingEmbed); + await botMsg.react("✅"); + await botMsg.react("❌"); + + botMsg.awaitReactions(filter, { max: 1, time: 30000, errors: ["time"] }) + .then(async collected => { + if (collected.first().emoji.name == "❌") { + // user react with ❌ + await botMsg.reactions.removeAll(); + console.log("> Command canceled."); + return replyChannel.send(cancelEmbed); + } + else if (!checkIsMember) { + // user react with ✅ + // the user that has thread not in main server + await botMsg.reactions.removeAll(); + console.log("> User isn't a member."); + return replyChannel.send(notMemberEmbed); + } + else if(checkIsBlocked) { + // the user that has thread are blocked + await botMsg.reactions.removeAll(); + console.log("> User are blocked."); + return replyChannel.send(blockedEmbed); + } + else { + // user are member and not blocked + const getUser = await mainServer.members.cache.get(userID).user; + const userDMEmbed = getEmbed.execute(param, "", config.sent_color, "Message Received", isTag, "", mainServer); + const threadChannelEmbed = getEmbed.execute(param, author, config.sent_color, "Tag Message Sent", isTag, "", getUser); + + try{ + await getUser.send(userDMEmbed); + } + catch (error) { + if(error.message == "Cannot send messages to this user") { + await botMsg.reactions.removeAll(); + console.log("> Recipient's DM are disabled."); + return channel.send(noDMEmbed); + } + } + await channel.send(threadChannelEmbed); + return message.delete().then(botMsg.delete()); + } + }) + .catch(async () => { + await botMsg.reactions.removeAll(); + console.log("> Timeout."); + return replyChannel.send(timeoutEmbed); + }); + } + break; + } } - } + }, }; diff --git a/commands/tagadd.js b/commands/tagadd.js deleted file mode 100644 index a6bc0a7..0000000 --- a/commands/tagadd.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'tagadd', - aliases: ['addtag', 'newtag', 't+'], - level: 'Moderator', - guildOnly: true, - args: true, - usage: '[tag name]', - description: 'Add a saved response.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const tagadd = param.tagadd; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return tagadd.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return tagadd.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return tagadd.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return tagadd.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/tagdelete.js b/commands/tagdelete.js deleted file mode 100644 index e59a96d..0000000 --- a/commands/tagdelete.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'tagdelete', - aliases: ['tagdel', 'deltag', 't-'], - level: 'Moderator', - guildOnly: true, - args: true, - usage: '[tag name]', - description: 'Delete a saved response.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const tagdelete = param.tagdelete; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return tagdelete.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return tagdelete.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return tagdelete.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return tagdelete.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/tagedit.js b/commands/tagedit.js deleted file mode 100644 index 9de14c8..0000000 --- a/commands/tagedit.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'tagedit', - aliases: ['edittag', 't='], - level: 'Moderator', - guildOnly: true, - args: true, - usage: '[tag name]', - description: 'Edit a saved response.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const tagedit = param.tagedit; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return tagedit.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return tagedit.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return tagedit.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return tagedit.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/taginfo.js b/commands/taginfo.js deleted file mode 100644 index 2c93186..0000000 --- a/commands/taginfo.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'taginfo', - aliases: ['infotag', 't?'], - level: 'Moderator', - guildOnly: true, - args: true, - usage: '[tag name]', - description: 'Show a saved response information.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const taginfo = param.taginfo; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return taginfo.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return taginfo.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return taginfo.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return taginfo.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/taglist.js b/commands/taglist.js deleted file mode 100644 index 92ba2cc..0000000 --- a/commands/taglist.js +++ /dev/null @@ -1,54 +0,0 @@ -module.exports = { - name: 'taglist', - aliases: ['tags'], - level: 'Moderator', - guildOnly: true, - args: false, - usage: false, - description: 'Show all tag names.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const db = param.db; - const tagPrefix = param.dbPrefix.tag; - - const tagCollection = await db.list(tagPrefix); - const tagList = tagCollection.map(tag => `\`${tag.slice(tagPrefix.length)}\``).join(', ') || "No available tag"; - - const tagListEmbed = getEmbed.execute(param, config.info_color, "Tags", tagList); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return message.channel.send(tagListEmbed); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return message.channel.send(tagListEmbed); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return message.channel.send(tagListEmbed); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return message.channel.send(tagListEmbed); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/thread.js b/commands/thread.js new file mode 100644 index 0000000..61fc734 --- /dev/null +++ b/commands/thread.js @@ -0,0 +1,93 @@ +module.exports = { + name: "thread", + aliases: false, + level: "Moderator", + guildOnly: true, + args: true, + reqConfig: ["mainServerID"], // Configs needed to run this command. + usage: [" ", " [page number]"], + description: "Show a user thread information or list of open thread(s).", + note: false, + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; + const getEmbed = param.getEmbed; + const config = param.config; + const db = param.db; + const threadPrefix = param.dbPrefix.thread; + const firstArg = args.shift(); + + if(firstArg == "info" || firstArg == "i" || firstArg == "?") { + const mainServerID = config.mainServerID; + const mainServer = await client.guilds.cache.get(mainServerID); + + const userID = args.shift(); + const dbKey = threadPrefix + userID; + const isThread = await db.get(dbKey); + + const noThreadEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", "Couldn't find any thread asociated with that user id."); + + if (!isThread) { + console.log("> Thread not found."); + return replyChannel.send(noThreadEmbed); + } + else { + const threadData = []; + const member = mainServer.members.cache.get(userID); + + const temp = isThread.split("-"); + const channelID = temp.shift(); + threadData.push(`${temp.join("-")}`); + if (member) { + threadData.push(`**User Tag** : \`${member.user.tag}\``); + } + else { + threadData.push("**User Tag** : `Couldn't find user at main server.`"); + } + threadData.push(`**User ID** : \`${userID}\``); + threadData.push(`**Thread Channel** : <#${channelID}>`); + + const threadInfoEmbed = getEmbed.execute(param, "", config.info_color, "Thread Information", threadData.join("\n")); + return replyChannel.send(threadInfoEmbed); + } + } + else if(firstArg == "list" || firstArg == "l") { + let pageNumber = args.shift(); + + const threadlist = await db.list(threadPrefix); + let pages = Math.floor(threadlist.length / 20); + if (threadlist.length % 20 != 0) { + // add 1 number of pages if residual quotient is not 0 (15%10=5 -> 5 > 0) + pages += 1; + } + if (threadlist.length == 0) { + pageNumber = 0; + } + else if(!pageNumber || isNaN(pageNumber) || pageNumber <= 0) { + // user didn't gave input or input is not a number or input is below or same as 0 + pageNumber = 1; + } + else if(pageNumber > pages) { + // input is higher than the number of pages + pageNumber = pages; + } + + const listArray = threadlist.map(key => { + const cleanKey = key.slice(threadPrefix.length); + return `🔹 <@${cleanKey}> (\`${cleanKey}\`)`; + }); + const firstIndex = Math.abs((pageNumber - 1) * 20); + let listString = listArray.slice(firstIndex, firstIndex + 20).join("\n") || "`List empty.`"; + if (pages > 1) { + listString += `\n\`Page ${pageNumber} from ${pages} pages\``; + } + else { + listString += `\n\`Page ${pageNumber} from ${pages} page\``; + } + + const listEmbed = getEmbed.execute(param, "", config.info_color, "Open Threads", listString); + return replyChannel.send(listEmbed); + } + }, +}; diff --git a/commands/threadinfo.js b/commands/threadinfo.js deleted file mode 100644 index 47aa845..0000000 --- a/commands/threadinfo.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = { - name: 'threadinfo', - aliases: false, - level: 'Moderator', - guildOnly: true, - args: true, - usage: '', - description: 'Show a user thread information.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const threadinfo = param.threadinfo; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - return message.channel.send(noServerEmbed); - } else if(message.author.id === config.botOwnerID) { - // bot owner - return threadinfo.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return threadinfo.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return threadinfo.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/threadlist.js b/commands/threadlist.js deleted file mode 100644 index 8e59332..0000000 --- a/commands/threadlist.js +++ /dev/null @@ -1,49 +0,0 @@ -module.exports = { - name: 'threadlist', - aliases: ['threads'], - level: 'Moderator', - guildOnly: true, - args: false, - usage: '[page number]', - description: 'Show all open threads.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - const threadlist = param.threadlist; - - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); - - if (message.author.id === config.botOwnerID) { - // bot owner - return threadlist.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return threadlist.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return threadlist.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return threadlist.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); - } - } -}; diff --git a/commands/turnoff.js b/commands/turnoff.js index 7facc5c..c8fdab7 100644 --- a/commands/turnoff.js +++ b/commands/turnoff.js @@ -1,28 +1,25 @@ -module.exports = { - name: 'turnoff', - aliases: ['shutdown', 'stop'], - level: 'Owner', - guildOnly: true, - args: false, - usage: false, - description: 'Turn off the bot.', - note: false, - async execute(param, message, args) { - const config = param.config; - const getEmbed = param.getEmbed; - - const successEmbed = getEmbed.execute(param, config.info_color, "Turning Off", `**Turning off in** : **${Math.round(param.client.ws.ping)}** ms`); - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - - if (message.author.id === config.botOwnerID) { - // bot owner - console.log("Turning Off..."); - message.channel.send(successEmbed).then(() => { - process.exit(1); - }); - } else { - // Not bot owner - return message.channel.send(noPermEmbed); - } - } -}; +module.exports = { + name: "turnoff", + aliases: ["shutdown", "stop"], + level: "Owner", + guildOnly: true, + args: false, + reqConfig: false, // Configs needed to run this command. + usage: false, + description: "Turn off the bot.", + note: false, + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const process = param.process; + const config = param.config; + const getEmbed = param.getEmbed; + + const successEmbed = getEmbed.execute(param, "", config.info_color, "Turning Off", `**Turning off in** : **${Math.round(param.client.ws.ping)}** ms`); + + console.log(">>> Turning Off <<<"); + replyChannel.send(successEmbed).then(() => { + process.exit(1); + }); + }, +}; diff --git a/commands/unblock.js b/commands/unblock.js index e433264..1b07660 100644 --- a/commands/unblock.js +++ b/commands/unblock.js @@ -1,49 +1,37 @@ module.exports = { - name: 'unblock', + name: "unblock", aliases: false, - level: 'Moderator', + level: "Moderator", guildOnly: true, args: true, - usage: '', - description: 'Unblock user from creating new thread.', + reqConfig: false, // Configs needed to run this command. + usage: [""], + description: "Unblock user from creating new thread.", note: false, - async execute(param, message, args) { + async execute(param, message, args, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const config = param.config; + const db = param.db; + const blockPrefix = param.dbPrefix.block; const getEmbed = param.getEmbed; - const unblock = param.unblock; - const noPermEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", "You don't have permission to run this command."); - const noServerEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`mainServerID` and/or `threadServerID` value is empty."); - const noAdminEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", "`adminRoleID` and/or `modRoleID` value is empty."); + const userID = args.shift(); + + const notFoundEmbed = getEmbed.execute(param, "", config.error_color, "Not Found", `<@${userID}> (\`${userID}\`) isn't blocked.`); + const successEmbed = getEmbed.execute(param, "", config.info_color, "Success", `Succesfully unblock <@${userID}> (\`${userID}\`).`); - if (message.author.id === config.botOwnerID) { - // bot owner - return unblock.execute(param, message, args); - } else if (config.mainServerID == "empty" && config.threadServerID == "empty" && message.member.hasPermission("ADMINISTRATOR")) { - // mainServerID and threadServerID empty and user has ADMINISTRATOR permission - message.channel.send(noServerEmbed); - return unblock.execute(param, message, args); - } else if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside main server or thread server - if (config.adminRoleID == "empty" || config.modRoleID == "empty") { - // adminRoleID empty - message.channel.send(noAdminEmbed); - } - if (message.member.hasPermission("ADMINISTRATOR") || await param.roleCheck.execute(message, config.adminRoleID)) { - // user has ADMINISTRATOR permission or has admin role - return unblock.execute(param, message, args); - } else if (await param.roleCheck.execute(message, config.modRoleID)) { - // user has moderator role - return unblock.execute(param, message, args); - } else if (config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - // user didn't have ADMINISTRATOR permission nor has admin role - return; - } else { - return message.channel.send(noPermEmbed); - } - } else { - // outside main server and thread server - return message.channel.send(noPermEmbed); + const dbKey = blockPrefix + userID; + const blockData = await db.get(dbKey); + if(blockData) { + db.delete(dbKey).then(() => { + console.log(`> Unblocked ${userID}.`); + return replyChannel.send(successEmbed); + }); + } + else { + console.log("> Data not found."); + return replyChannel.send(notFoundEmbed); } - } + }, }; diff --git a/config.json b/config.json index 89b227a..aab330e 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "prefix" : "=", + "prefix" : ".", "botOwnerID" : "", "cooldown" : "0", "maintenance" : "0", diff --git a/events/guildCreate.js b/events/guildCreate.js new file mode 100644 index 0000000..ad0d036 --- /dev/null +++ b/events/guildCreate.js @@ -0,0 +1,18 @@ +module.exports = { + name: "guildCreate", + once: false, + disabled: false, // Change to 'true' to disable this event. + async execute(param, ...args) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const guild = args[0]; + const config = param.config; + const owner = param.client.users.fetch(config.botOwnerID); + + if(owner) { + const newServerEmbed = param.getEmbed.execute(param, guild, config.info_color, "Joined a Guild", `[**${guild.name}**] (\`${guild.id}\`)`); + owner.send(newServerEmbed); + } + console.log(`> Joined [${guild.name}] guild.`); + }, +}; \ No newline at end of file diff --git a/events/message.js b/events/message.js new file mode 100644 index 0000000..cbf7324 --- /dev/null +++ b/events/message.js @@ -0,0 +1,212 @@ +module.exports = { + name: "message", + once: false, + disabled: false, // Change to 'true' to disable this event. + async execute(param, ...args) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const Discord = param.Discord; + const client = param.client; + const cooldowns = param.cooldowns; + const config = param.config; + const db = param.db; + const dbPrefix = param.dbPrefix; + const getEmbed = param.getEmbed; + const message = args[0]; + const author = message.author; + const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + + if(author.bot) return console.log(`> ${author.tag} is a bot.`); + let now = Date.now(); + let timestamps, cooldownAmount; + + // Activity Cooldown + if (!cooldowns.has("botActivity")) { + cooldowns.set("botActivity", new Discord.Collection()); + } + timestamps = cooldowns.get("botActivity"); + cooldownAmount = 10000; // 10 seconds + if (!timestamps.has("botActivity")) { + await param.updateActivity.execute(param); + } + timestamps.set("botActivity", now); + setTimeout(() => timestamps.delete("botActivity"), cooldownAmount); + // ---------------------------- + + // Permission Check + const guildID = message.guild ? message.guild.id : null; + param.isOwner = author.id === config.botOwnerID; + let adminPerm = false; + let adminRole = false; + if(guildID) { + // If message is inside a guild, check author administrator permission. + adminPerm = message.member.hasPermission("ADMINISTRATOR"); + } + if(config.adminRoleID != "empty" && guildID) { + // If adminRoleID isn't empty and message inside a guild, check whether author have admin role or not. + adminRole = message.member.roles.cache.get(config.adminRoleID) ? true : false; + } + if (config.mainServerID == "empty" && config.threadServerID == "empty") { + // If main and thread server isn't set yet, check whether author have admin permission or admin role. + param.isAdmin = adminPerm || adminRole; + } + else { + // Else, check whether the message also come from main or thread server. + param.isAdmin = (guildID == config.mainServerID || guildID == config.threadServerID) && (adminPerm || adminRole); + } + if(config.modRoleID != "empty" && guildID) { + // If adminRoleID isn't empty and message inside a guild, check whether author have moderator role or not. + param.isModerator = message.member.roles.cache.get(config.modRoleID) ? true : false; + } + // ---------------------------- + + // Deciding channel to reply + const guild = message.guild; + const channel = message.channel; + const mainServerID = config.mainServerID; + const threadServerID = config.threadServerID; + const botChannelID = config.botChannelID; + + let replyChannel; + if(guild) { + // If it's inside a guild + if ((mainServerID == "empty" || threadServerID == "empty" || guild.id == mainServerID || guild.id == threadServerID) && (botChannelID == "empty" || channel.id == botChannelID || channel.parent == config.categoryID)) { + // If it's either inside main or thread server or when it's not set yet, and it's inside a bot or thread channel or if bot channel is empty. + replyChannel = message.channel; + } + else { + // Else, send the reply through DM. + replyChannel = author; + } + } + else { + // Else, send the reply through DM. + replyChannel = author; + } + // ------------------------- + + try { + const maintenanceEmbed = getEmbed.execute(param, "", config.error_color, "Maintenance", "All functions are disabled."); + + // checking whether user use prefix or mention the bot + const prefixRegex = new RegExp(`^(<@!?${client.user.id}>|${escapeRegex(config.prefix)})\\s*`); + if (!prefixRegex.test(message.content)) { + // user didn't use prefix or mention the bot + console.log("> No prefix."); + if(guild) { + // message are inside a guild + return console.log("> Message inside a guild, ignored."); + } + else { + // Direct Message + const isThread = await db.get(dbPrefix.thread + author.id); + if(!isThread) { + // User didn't have any open thread + return console.log("> User doesn't have an open thread, ignored."); + } + else if(config.maintenance == "1" && !param.isOwner && !param.isAdmin) { + // Maintenance mode enabled + replyChannel.send(maintenanceEmbed).then(() => { + return console.log("> Maintenance mode enabled, ignored."); + }); + } + else { + // User have open thread and maintenance mode disabled + const temp = isThread.split("-"); + const thread = { + channelID: temp.shift(), + threadTitle: temp.join("-"), + }; + return param.userReply.execute(param, message, thread); + } + } + } + + if(config.maintenance == "1" && !param.isOwner && !param.isAdmin) { + // Maintenance mode enabled + replyChannel.send(maintenanceEmbed).then(() => { + return console.log("> Maintenance mode enabled, ignored."); + }); + } + + const [, matchedPrefix] = message.content.match(prefixRegex); + const msgArgs = message.content.slice(matchedPrefix.length).trim().split(/ +/); + const commandName = msgArgs.shift().toLowerCase(); + // finding command that was triggered + const command = client.commands.get(commandName) || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName)); + + // user using prefix or mention bot, command name is invalid + if (!command) return console.log(`> ${commandName} isn't a command.`); + console.log(`> ${author.tag}(${author.id}) called ${commandName} command.`); + param.cmdName = commandName; + + // cooldown more than 0 + if (config.cooldown > 0) { + if (!cooldowns.has(command.name)) { + cooldowns.set(command.name, new Discord.Collection()); + } + + now = Date.now(); + timestamps = cooldowns.get(command.name); + cooldownAmount = config.cooldown * 1000; + + if (timestamps.has(author.id)) { + const expirationTime = timestamps.get(author.id) + cooldownAmount; + + if (now < expirationTime) { + // Uncomment 3 line of codes below so the bot send message when the cooldown isn't over. + /* + const timeLeft = (expirationTime - now) / 1000; + const embed = param.getEmbed.execute(param, "", config.info_color, "Cooldown", `${timeLeft.toFixed(1)} second(s).`) + replyChannel.send(embed); + */ + return console.log("> Command still in cooldown."); + } + } + + timestamps.set(author.id, now); + setTimeout(() => timestamps.delete(author.id), cooldownAmount); + } + + // command is guildOnly, user trigger it inside Direct Message + if(command.guildOnly && message.channel.type !== "text" && author.id != config.botOwnerID) { + const noDMEmbed = param.getEmbed.execute(param, "", config.error_color, "Command Unavailable", "This command can't be used inside Direct Message."); + console.log("> Guild only command triggered in DM."); + return replyChannel.send(noDMEmbed); + } + + // command need arguments to run, user did't gave any + if(command.args && !msgArgs.length) { + let description = "You didn't provide any arguments."; + + if(command.usage) { + const usages = []; + for (let i = 0;i < command.usage.length; i++) { + usages.push(`\`${config.prefix}${command.name} ${command.usage[i]}\``); + } + description += `\n**Usage** : ${usages.join(", ")}`; + } + if(command.note) { + description += `\n**Note** : \`${command.note}\``; + } + + const noArgsEmbed = param.getEmbed.execute(param, "", config.warning_color, "Missing Arguments", description); + console.log("> Missing Arguments."); + return replyChannel.send(noArgsEmbed); + } + await param.commandHandler.execute(param, message, msgArgs, command, replyChannel); + } + catch (error) { + // catching error -> log it in console -> send error message to user + if(error.message == "Cannot send messages to this user") { + return console.log(`>> ${error.message} <<`); + } + else { + const errorEmbed = param.getEmbed.execute(param, "", config.error_color, "An Error Occured.", `**Contact bot Owner** : <@${config.botOwnerID}>\n**Error Name** : \`${error.name}\`\n**Error Message** : \`${error.message}\``); + replyChannel.send(errorEmbed).then(() => { + return console.log(error); + }); + } + } + }, +}; \ No newline at end of file diff --git a/events/ready.js b/events/ready.js new file mode 100644 index 0000000..44d8ce1 --- /dev/null +++ b/events/ready.js @@ -0,0 +1,15 @@ +module.exports = { + name: "ready", + once: false, + disabled: false, // Change to 'true' to disable this event. + async execute(param) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + console.log(`> Logged in as ${param.client.user.tag}.`); + + await param.configSync.execute(param); + + setTimeout(async ()=> { + await param.updateActivity.execute(param); + }, 5000); + }, +}; \ No newline at end of file diff --git a/functions/acloseFn.js b/functions/acloseFn.js deleted file mode 100644 index 1514a02..0000000 --- a/functions/acloseFn.js +++ /dev/null @@ -1,76 +0,0 @@ -module.exports = { - name: "aclose", - async execute(param, message, args) { - const Discord = param.Discord; - const client = param.client; - const getEmbed = param.getEmbed; - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - const updateActivity = param.updateActivity; - - const mainServerID = config.mainServerID; - const mainServer = await client.guilds.cache.get(mainServerID); - const threadServerID = config.threadServerID; - const threadServer = await client.guilds.cache.get(threadServerID); - const logChannelID = config.logChannelID; - const logChannel = await threadServer.channels.cache.get(logChannelID); - const author = message.author; - const channel = message.channel; - - const userID = channel.name.split("-").pop(); - const isThread = await db.get(threadPrefix + userID); - const addSpace = args.join(' '); - const deleteSeparator = addSpace.split(/-+/); - const reason = deleteSeparator.shift(); - const note = deleteSeparator.shift() || "empty"; - - const noThreadEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find any thread asociated with this channel.`); - - if (!isThread) { - return channel.send(noThreadEmbed); - } else { - const temp = isThread.split("-"); - temp.shift(); - const threadTitle = temp.join("-"); - const user = await client.users.cache.get(userID); - const logDescription = `${threadTitle}\n**Reason** : ${reason}\n**Note** : ${note}`; - const userDescription = `${threadTitle}\n**Reason** : ${reason}`; - - let logEmbed; - const userDMEmbed = new Discord.MessageEmbed() - .setColor(config.warning_color) - .setAuthor("[Anonymous]") - .setTitle("Thread Closed") - .setDescription(userDescription) - .setFooter(mainServer.name, mainServer.iconURL()) - .setTimestamp(); - - if (user) { - logEmbed = new Discord.MessageEmbed() - .setColor(config.warning_color) - .setAuthor(`[Anonymous] | ${author.tag}`, author.avatarURL()) - .setTitle("Thread Closed") - .setDescription(logDescription) - .setFooter(`${user.tag} | ${user.id}`, user.avatarURL()) - .setTimestamp(); - await user.send(userDMEmbed); - await logChannel.send(logEmbed); - } else { - logEmbed = new Discord.MessageEmbed() - .setColor(config.warning_color) - .setAuthor(`[Anonymous] | ${author.tag}`, author.avatarURL()) - .setTitle("Thread Closed") - .setDescription(logDescription) - .setFooter(`Can't find user | ${userID}`) - .setTimestamp(); - await logChannel.send(logEmbed); - } - - db.delete(threadPrefix + userID).then(() => console.log(`Closing Thread.`)); - await updateActivity.execute(param); - return channel.delete(); - } - - } -}; diff --git a/functions/areplyFn.js b/functions/areplyFn.js deleted file mode 100644 index c908a4f..0000000 --- a/functions/areplyFn.js +++ /dev/null @@ -1,83 +0,0 @@ -module.exports = { - name: "areply", - async execute(param, message, args) { - const Discord = param.Discord; - const MessageAttachment = param.MessageAttachment; - const client = param.client; - const getEmbed = param.getEmbed; - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - const isMember = param.isMember; - const isBlocked = param.isBlocked; - - const mainServerID = config.mainServerID; - const mainServer = await client.guilds.cache.get(mainServerID); - const author = message.author; - const channel = message.channel; - - const userID = channel.name.split("-").pop(); - const isThread = await db.get(threadPrefix + userID); - const checkIsBlocked = await isBlocked.execute(param, author.id); - - const blockedEmbed = getEmbed.execute(param, config.error_color, "Blocked", `User blocked.`); - const noDMEmbed = getEmbed.execute(param, config.error_color, "Not Sent", `User disabled Direct Message.`); - const noUserEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find user in my collection.`); - const noThreadEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find any thread asociated with this channel.`); - - if (!isThread) { - return channel.send(noThreadEmbed); - } else { - const checkIsMember = await isMember.execute(param, userID); - const notMemberEmbed = getEmbed.execute(param, config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); - - if (!checkIsMember) { - return channel.send(notMemberEmbed); - } else if (checkIsBlocked) { - return channel.send(blockedEmbed); - } else { - const member = await mainServer.members.cache.get(userID); - - if (!member) { - return channel.send(noUserEmbed); - } else { - const user = member.user; - const description = args.join(' '); - const userDMEmbed = new Discord.MessageEmbed() - .setColor(config.sent_color) - .setAuthor("[Anonymous]") - .setTitle("Message Received") - .setDescription(description) - .setFooter(mainServer.name, mainServer.iconURL()) - .setTimestamp(); - const threadChannelEmbed = new Discord.MessageEmbed() - .setColor(config.sent_color) - .setAuthor(`[Anonymous] | ${author.tag}`, author.avatarURL()) - .setTitle("Message Sent") - .setDescription(description) - .setFooter(`${user.tag} | ${user.id}`, user.avatarURL()) - .setTimestamp(); - - try{ - await user.send(userDMEmbed); - } catch (error) { - if(error.message == "Cannot send messages to this user") { - return channel.send(noDMEmbed); - } - } - await channel.send(threadChannelEmbed); - if (message.attachments.size > 0) { - await message.attachments.forEach(async atch => { - const attachment = new MessageAttachment(atch.url); - await user.send(attachment); - await channel.send(attachment); - }); - } - return message.delete(); - } - - } - } - - } -}; diff --git a/functions/bindFn.js b/functions/bindFn.js deleted file mode 100644 index b9626d1..0000000 --- a/functions/bindFn.js +++ /dev/null @@ -1,44 +0,0 @@ -module.exports = { - name: "bind", - async execute(param, message, args) { - // Due to database limitation, i didn't check whether the channel tried to be binded already tied to other thread or not. - const client = param.client; - const getEmbed = param.getEmbed; - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - const updateActivity = param.updateActivity; - - const threadServerID = config.threadServerID; - const threadServer = await client.guilds.cache.get(threadServerID); - const categoryID = config.categoryID; - const logChannelID = config.logChannelID; - - const userID = args.shift(); - const channelID = args.shift(); - const dbKey = threadPrefix + userID; - const isThread = await db.get(dbKey); - const getChannel = await threadServer.channels.cache.get(channelID); - - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Binded <@${userID}> (\`${userID}\`) thread to <#${channelID}>.`); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find that channel.`); - const notChannelEmbed = getEmbed.execute(param, config.error_color, "Invalid Channel", `That isn't thread channel.`); - - if(!getChannel) { - return message.channel.send(noChannelEmbed); - } else if(getChannel.parentID != categoryID || channelID == categoryID || channelID == logChannelID) { - return message.channel.send(notChannelEmbed); - } else if(!isThread) { - await db.set(dbKey, `${channelID}-empty`); - await updateActivity.execute(param); - return message.channel.send(successEmbed); - } else { - isThread.split("-").shift(); - isThread = isThread.join("-"); - await db.set(dbKey, `${channelID}-${isThread}`); - await updateActivity.execute(param); - return message.channel.send(successEmbed); - } - - } -}; diff --git a/functions/blockFn.js b/functions/blockFn.js deleted file mode 100644 index 366fbd7..0000000 --- a/functions/blockFn.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = { - name: "block", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const blockPrefix = param.dbPrefix.block; - const getEmbed = param.getEmbed; - - const modID = message.author.id; - const userID = args.shift(); - const reason = args.join(' ') || "empty"; - - const duplicatedEmbed = getEmbed.execute(param, config.error_color, "Duplicated", `<@${userID}> (\`${userID}\`) already blocked.`); - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Succesfully block <@${userID}> (\`${userID}\`).`); - - const dbKey = blockPrefix + userID; - const isBlocked = await db.get(dbKey); - if(isBlocked){ - console.log(`User [${userID}] is already blocked.`); - return message.channel.send(duplicatedEmbed); - } else { - db.set(dbKey, `${modID}-${reason}`).then(() => { - console.log(`Blocked [${userID}].`); - return message.channel.send(successEmbed); - }) - } - } -}; diff --git a/functions/blockinfoFn.js b/functions/blockinfoFn.js deleted file mode 100644 index 3e3ab75..0000000 --- a/functions/blockinfoFn.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - name: "blockinfo", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const blockPrefix = param.dbPrefix.block; - const getEmbed = param.getEmbed; - - const userID = args.shift(); - - const notFoundEmbed = getEmbed.execute(param, config.error_color, "Not Found", `<@${userID}> (\`${userID}\`) isn't blocked.`); - - const blockData = await db.get(blockPrefix + userID); - if(blockData) { - const temp = blockData.split("-"); - const modID = temp.shift(); - const reason = temp.join("-"); - const data = []; - data.push(`**User** : <@${userID}> [\`${userID}\`]`); - data.push(`**Moderator** : <@${modID}> [\`${modID}\`]`); - data.push(`**Reason** : ${reason}`); - const infoEmbed = getEmbed.execute(param, config.info_color, "Block Info", data.join('\n')); - return message.channel.send(infoEmbed); - } else { - return message.channel.send(notFoundEmbed); - } - - } -}; diff --git a/functions/blocklistFn.js b/functions/blocklistFn.js deleted file mode 100644 index b33617d..0000000 --- a/functions/blocklistFn.js +++ /dev/null @@ -1,40 +0,0 @@ -module.exports = { - name: "blocklist", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const blockPrefix = param.dbPrefix.block; - const getEmbed = param.getEmbed; - - let pageNumber = args.shift(); - - const blocklist = await db.list(blockPrefix); - let pages = Math.floor(blocklist.length / 20); - if (blocklist.length % 20 != 0) { - // add 1 number of pages if residual quotient is not 0 (15%10=5 -> 5 > 0) - pages += 1; - } - if (blocklist.length == 0) { - pageNumber = 0; - } else if(!pageNumber || isNaN(pageNumber) || pageNumber <= 0) { - // user didn't gave input or input is not a number or input is below or same as 0 - pageNumber = 1; - } else if(pageNumber > pages) { - // input is higher than the number of pages - pageNumber = pages; - } - - const listArray = blocklist.map(block =>`🔸 <@${block.slice(blockPrefix.length)}> (\`${block.slice(blockPrefix.length)}\`)`); - const firstIndex = Math.abs((pageNumber - 1) * 20); - let listString = listArray.slice(firstIndex, firstIndex + 20).join("\n") || `\`List empty.\``; - if (pages > 1) { - listString += `\n\`Page ${pageNumber} from ${pages} pages\``; - } else { - listString += `\n\`Page ${pageNumber} from ${pages} page\``; - } - - const listEmbed = getEmbed.execute(param, config.info_color, "Blocked Users", listString); - return message.channel.send(listEmbed); - - } -}; diff --git a/functions/closeFn.js b/functions/closeFn.js deleted file mode 100644 index 47cf933..0000000 --- a/functions/closeFn.js +++ /dev/null @@ -1,76 +0,0 @@ -module.exports = { - name: "close", - async execute(param, message, args) { - const Discord = param.Discord; - const client = param.client; - const getEmbed = param.getEmbed; - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - const updateActivity = param.updateActivity; - - const mainServerID = config.mainServerID; - const mainServer = await client.guilds.cache.get(mainServerID); - const threadServerID = config.threadServerID; - const threadServer = await client.guilds.cache.get(threadServerID); - const logChannelID = config.logChannelID; - const logChannel = await threadServer.channels.cache.get(logChannelID); - const author = message.author; - const channel = message.channel; - - const userID = channel.name.split("-").pop(); - const isThread = await db.get(threadPrefix + userID); - const addSpace = args.join(' '); - const deleteSeparator = addSpace.split(/-+/); - const reason = deleteSeparator.shift(); - const note = deleteSeparator.shift() || "empty"; - - const noThreadEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find any thread asociated with this channel.`); - - if (!isThread) { - return channel.send(noThreadEmbed); - } else { - const temp = isThread.split("-"); - temp.shift(); - const threadTitle = temp.join("-"); - const user = await client.users.cache.get(userID); - const logDescription = `${threadTitle}\n**Reason** : ${reason}\n**Note** : ${note}`; - const userDescription = `${threadTitle}\n**Reason** : ${reason}`; - - let logEmbed; - const userDMEmbed = new Discord.MessageEmbed() - .setColor(config.warning_color) - .setAuthor(author.tag, author.avatarURL()) - .setTitle("Thread Closed") - .setDescription(userDescription) - .setFooter(mainServer.name, mainServer.iconURL()) - .setTimestamp(); - - if (user) { - logEmbed = new Discord.MessageEmbed() - .setColor(config.warning_color) - .setAuthor(author.tag, author.avatarURL()) - .setTitle("Thread Closed") - .setDescription(logDescription) - .setFooter(`${user.tag} | ${user.id}`, user.avatarURL()) - .setTimestamp(); - await user.send(userDMEmbed); - await logChannel.send(logEmbed); - } else { - logEmbed = new Discord.MessageEmbed() - .setColor(config.warning_color) - .setAuthor(author.tag, author.avatarURL()) - .setTitle("Thread Closed") - .setDescription(logDescription) - .setFooter(`Can't find user | ${userID}`) - .setTimestamp(); - await logChannel.send(logEmbed); - } - - db.delete(threadPrefix + userID).then(() => console.log(`Closing Thread.`)); - await updateActivity.execute(param); - return channel.delete(); - } - - } -}; diff --git a/functions/commandHandler.js b/functions/commandHandler.js new file mode 100644 index 0000000..2aad963 --- /dev/null +++ b/functions/commandHandler.js @@ -0,0 +1,96 @@ +module.exports = { + name: "commandHandler", + async execute(param, message, msgArgs, command, replyChannel) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + + const client = param.client; + const getEmbed = param.getEmbed; + const config = param.config; + + const isOwner = param.isOwner; + const isAdmin = param.isAdmin; + const isModerator = param.isModerator; + + const reqConfig = command.reqConfig; + const data = []; + + if(reqConfig) { + let owner = false, mainServer = false, threadServer = false, category = false, logChannel = false; + if(config.botOwnerID !== "empty") { + owner = await client.users.fetch(config.botOwnerID); + } + if(config.mainServerID !== "empty") { + mainServer = client.guilds.cache.get(config.mainServerID); + } + if(config.threadServerID !== "empty") { + threadServer = client.guilds.cache.get(config.threadServerID); + } + if(threadServer) { + category = threadServer.channels.cache.get(config.categoryID); + logChannel = threadServer.channels.cache.get(config.logChannelID); + } + reqConfig.forEach(configName => { + switch(configName) { + case "ownerID": + if(!owner) data.push(`- \`${configName}\``); + break; + case "mainServerID": + if(!mainServer) data.push(`- \`${configName}\``); + break; + case "threadServerID": + if(!threadServer) data.push(`- \`${configName}\``); + break; + case "categoryID": + if(!category) data.push(`- \`${configName}\``); + break; + case "logChannelID": + if(!logChannel) data.push(`- \`${configName}\``); + break; + default: + break; + } + }); + } + + const requiredEmbed = getEmbed.execute(param, "", config.warning_color, "Required Configuration", `The following configuration cannot be empty or invalid : \n${data.join("\n")}`); + const noPermEmbed = getEmbed.execute(param, "", config.warning_color, "Missing Permission", "You don't have permission to run this command."); + + switch(command.level) { + case "Owner" : { + if (!isOwner) { + console.log("> Missing Permission."); + replyChannel.send(noPermEmbed); + break; + } + } + // eslint-disable-next-line no-fallthrough + case "Admin" : { + if (!isOwner && !isAdmin) { + console.log("> Missing Permission."); + replyChannel.send(noPermEmbed); + break; + } + } + // eslint-disable-next-line no-fallthrough + case "Moderator" : { + if (!isOwner && !isAdmin && !isModerator) { + console.log("> Missing Permission."); + replyChannel.send(noPermEmbed); + break; + } + } + // eslint-disable-next-line no-fallthrough + default : { + if (data.length !== 0) { + console.log(`> Configuration needed: ${data.join(", ")}.`); + replyChannel.send(requiredEmbed); + break; + } + else { + await command.execute(param, message, msgArgs, replyChannel); + break; + } + } + } + }, +}; \ No newline at end of file diff --git a/functions/configFn.js b/functions/configFn.js deleted file mode 100644 index f26fe09..0000000 --- a/functions/configFn.js +++ /dev/null @@ -1,38 +0,0 @@ -module.exports = { - name: "configFn", - async execute(param) { - const Discord = param.Discord; - const client = param.client; - const config = param.config; - // getting the name of each config (prefix, botOwnerID, etc) - const configKeys = Object.keys(config); - const botConfig = []; - const serverConfig = []; - const embedColorConfig = []; - // As separator for server related config and bot config. - const maintenanceIndex = configKeys.indexOf("maintenance"); - // As separator for server related config and embed color config. - const info_colorIndex = configKeys.indexOf("info_color"); - - for (let i = 0; i < configKeys.length; i++) { - if(i <= maintenanceIndex) { - botConfig.push(`${configKeys[i]} : \`${config[configKeys[i]]}\``); - } else if(i > maintenanceIndex && i < info_colorIndex) { - serverConfig.push(`${configKeys[i]} : \`${config[configKeys[i]]}\``); - } else if(i >= info_colorIndex && i < configKeys.length) { - embedColorConfig.push(`${configKeys[i]} : \`${config[configKeys[i]]}\``); - } - } - - const configEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("Configuration") - .addField("~ Bot ~", botConfig) - .addField("~ Server ~", serverConfig) - .addField("~ Embed Color ~", embedColorConfig) - .setThumbnail(client.user.avatarURL()) - .setFooter(client.user.tag, client.user.avatarURL()) - .setTimestamp(); - return configEmbed; - } -}; diff --git a/functions/configSync.js b/functions/configSync.js index c6e9271..5b2d404 100644 --- a/functions/configSync.js +++ b/functions/configSync.js @@ -1,37 +1,30 @@ module.exports = { name: "configSync", async execute(param) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const defConfig = param.defConfig; const config = param.config; const db = param.db; const configPrefix = param.dbPrefix.config; - console.log("[Syncing Configuration]"); const configKeys = Object.keys(config); const dbKeys = await db.list(configPrefix); - const syncPromise = new Promise(resolve => { - try { - async function forLoop() { - for (let i = 0; i < configKeys .length; i++) { - const dbKey = configPrefix + configKeys[i]; - if(!dbKeys.includes(dbKey)){ - console.log(`Resetting ${dbKey} value to ${defConfig[configKeys[i]]}.`); - await db.set(dbKey, defConfig[configKeys[i]]); - } - db.get(dbKey).then(async value => { - console.log(`Syncing ${configKeys[i]}: ${value}`); - config[configKeys[i]] = value || "empty"; - }); - } - resolve(); + async function forLoop() { + for (let i = 0; i < configKeys .length; i++) { + const dbKey = configPrefix + configKeys[i]; + if(!dbKeys.includes(dbKey)) { + console.log(`> Resetting ${dbKey} value to ${defConfig[configKeys[i]]}.`); + await db.set(dbKey, defConfig[configKeys[i]]); } - forLoop(); - } catch (error) { - return console.log(error); + await db.get(dbKey).then(value => { + console.log(`> Syncing ${configKeys[i]}: ${value}`); + config[configKeys[i]] = value || "empty"; + }); } + } + forLoop().then(async () => { + console.log(">> Synced <<"); }); - syncPromise.then(() => { - console.log("[Synced]"); - }); - } + }, }; diff --git a/functions/configinfoFn.js b/functions/configinfoFn.js deleted file mode 100644 index a6e0d42..0000000 --- a/functions/configinfoFn.js +++ /dev/null @@ -1,115 +0,0 @@ -module.exports = { - name: "configinfo", - async execute(param, message, args) { - const getEmbed = param.getEmbed; - const config = param.config; - - const configName = args.shift(); - const configCollection = await Object.keys(config); - const isConfig = await configCollection.includes(configName); - const configList = configCollection.map(conf => `\`${conf}\``).join(', '); - - const noConfigEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find config named \`${configName}\`.\nAvailable names : ${configList}`); - - const configData = []; - switch (configName) { - case "prefix": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : To differentiate between a command and non command.`); - configData.push(`**Requirements** : \n\`> Any input that didn't have [space] as it'll be ignored.\``); - break; - case "botOwnerID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : An owner of this bot can use any commands anywhere.`); - configData.push(`**Requirements** : \n\`> Only bot owner can change this value.\n> Input can't be empty.\``); - break; - case "cooldown": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Cooldown for each commands (in seconds).`); - configData.push(`**Requirements** : \n\`> Any number that's greater or equal to zero.\n> Input can't be empty.\``); - break; - case "maintenance": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Maintenance mode toggle. Config changed according previous value.`); - break; - case "mainServerID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : The server that is used by users who will use this bot to ask moderators.`); - configData.push(`**Requirements** : \n\`> Any server that have this bot.\n> Value can be same as [threadServerID].\``); - break; - case "threadServerID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : The server where thread channels will be on.`); - configData.push(`**Requirements** : \n\`> Any server that have this bot.\n> Value can be same as [mainServerID].\``); - break; - case "categoryID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Category channel where thread channels will be created.`); - configData.push(`**Requirements** : \n\`> Any category channel that are inside thread server.\n> [threadServerID] value can't be empty.\``); - configData.push(`**Note** : To understand what category channel is, check this [link](https://support.discordapp.com/hc/en-us/articles/115001580171-Channel-Categories-101).`); - configData.push(`\`ps. Discord.js treat it as channel that's why i use this term too.\``); - break; - case "logChannelID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Channel where thread logs will be sent.`); - configData.push(`**Requirements** : \n\`> Any channel inside thread server.\n> [threadServerID] value can't be empty.\``); - break; - case "adminRoleID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Role that will have administrator permission level.`); - configData.push(`**Requirements** : \n\`> Any role inside thread server.\n> [threadServerID] value can't be empty.\``); - break; - case "modRoleID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Role that will have moderator permission level.`); - configData.push(`**Requirements** : \n\`> Any role inside thread server.\n> [threadServerID] value can't be empty.\``); - break; - case "mentionedRoleID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : The role that will be mentioned on new thread.`); - configData.push(`**Requirements** : \n\`> Can be empty (no one mentioned).\n> Any role at thread server including here and everyone [set mentionedRoleID everyone].\``); - break; - case "botChannelID": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Channel where user can only use to execute commands (any commands in other channels will be ignored except help commands).`); - configData.push(`**Requirements** : \n\`> Can be empty (everyone can use any commands anywhere).\n> Any channels inside main server.\n> Shouldn't be a category channel (Discord.js treat categories as a channel too).\``); - break; - case "info_color": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Color used for any information related embeds.`); - configData.push(`**Requirements** : \n\`> Hex code color input.\``); - break; - case "warning_color": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Color used for any warning related embeds.`); - configData.push(`**Requirements** : \n\`> Hex code color input.\``); - break; - case "error_color": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Color used for any error related embeds.`); - configData.push(`**Requirements** : \n\`> Hex code color input.\``); - break; - case "sent_color": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Color used for any message sent on threads related embeds.`); - configData.push(`**Requirements** : \n\`> Hex code color input.\``); - break; - case "received_color": - configData.push(`**Name** : ${configName}`); - configData.push(`**Description** : Color used for any message received on threads related embeds.`); - configData.push(`**Requirements** : \n\`> Hex code color input.\``); - break; - default: - configData.push(`\`Information is still not available.\``); - } - - const dataEmbed = await getEmbed.execute(param, config.info_color, "Configuration Information", configData.join('\n')); - - if (!isConfig) { - return message.channel.send(noConfigEmbed); - } else { - return message.channel.send(dataEmbed); - } - - } -}; diff --git a/functions/getEmbed.js b/functions/getEmbed.js index 60428ef..2f430e7 100644 --- a/functions/getEmbed.js +++ b/functions/getEmbed.js @@ -1,12 +1,64 @@ module.exports = { name: "getEmbed", - execute(param, color, title, description) { - const embed = new param.Discord.MessageEmbed() - .setColor(color) - .setTitle(title) - .setDescription(description) - .setFooter(param.client.user.tag, param.client.user.avatarURL()) - .setTimestamp(); + execute(param, author, color, title, description, fields, footer, thumbnail) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + console.log(`> Title: ${title || "empty"}`); + + const embed = new param.Discord.MessageEmbed().setTimestamp(); + if (author) { + if (author.name) { + // author is a guild instance + embed.setAuthor(author.name, author.iconURL()); + } + else if (author.user) { + // author is a guild member instance + embed.setAuthor(author.user.tag, author.user.displayAvatarURL()); + } + else if (author.tag) { + // author is a user instance + embed.setAuthor(author.tag, author.displayAvatarURL()); + } + else { + // author is possibly a String + embed.setAuthor(author); + } + } + if (color) { + embed.setColor(color); + } + if (title) { + embed.setTitle(title); + } + if (description) { + embed.setDescription(description); + } + if (fields) { + fields.forEach(field => { + const splitted = field.split(";"); + embed.addField(splitted[0], splitted[1]); + }); + } + if (thumbnail) { + embed.setThumbnail(thumbnail); + } + if (footer) { + if (footer.user) { + // footer is member object + embed.setFooter(`${footer.user.tag} | ${footer.user.id}`, footer.user.displayAvatarURL()); + } + else if (footer.tag) { + // footer is user object + embed.setFooter(`${footer.tag} | ${footer.id}`, footer.displayAvatarURL()); + } + else if (footer.name) { + // footer is guild object + embed.setFooter(`${footer.name}`, footer.iconURL()); + } + else { + // footer is possibly a String + embed.setFooter(footer); + } + } return embed; - } + }, }; diff --git a/functions/isBlocked.js b/functions/isBlocked.js index 1328925..03fb2b9 100644 --- a/functions/isBlocked.js +++ b/functions/isBlocked.js @@ -1,14 +1,17 @@ module.exports = { name: "isBlocked", async execute(param, userID) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const db = param.db; const blockPrefix = param.dbPrefix.block; const isBlocked = await db.get(blockPrefix + userID); if(isBlocked) { return true; - } else { + } + else { return false; } - } + }, }; diff --git a/functions/isMember.js b/functions/isMember.js index 00ea4f4..1416556 100644 --- a/functions/isMember.js +++ b/functions/isMember.js @@ -1,15 +1,18 @@ module.exports = { name: "isMember", async execute(param, userID) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const mainServerID = param.config.mainServerID; const mainServer = await client.guilds.cache.get(mainServerID); - const isMember = await mainServer.members.cache.get(userID); + const isMember = await mainServer.members.fetch(userID); if(isMember) { return true; - } else { + } + else { return false; } - } + }, }; diff --git a/functions/newFn.js b/functions/newThread.js similarity index 51% rename from functions/newFn.js rename to functions/newThread.js index 02edc8b..97fb877 100644 --- a/functions/newFn.js +++ b/functions/newThread.js @@ -1,13 +1,15 @@ module.exports = { - name: "new", + name: "newThread", async execute(param, message, args) { - const Discord = param.Discord; + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const MessageAttachment = param.MessageAttachment; const moment = param.moment; const client = param.client; const config = param.config; const db = param.db; const threadPrefix = param.dbPrefix.thread; + const getEmbed = param.getEmbed; const updateActivity = param.updateActivity; const mainServerID = config.mainServerID; @@ -23,46 +25,29 @@ module.exports = { let mentionedRole = ""; if (mentionedRoleID == "everyone" || mentionedRoleID == "here") { mentionedRole = "@" + mentionedRoleID; - } else if (config.mentionedRoleID != null && config.mentionedRoleID != "empty") { + } + else if (config.mentionedRoleID != null && config.mentionedRoleID != "empty") { mentionedRole = "<@&" + mentionedRoleID + ">"; } - const channelName = author.tag.replace(/[^0-9a-z]/gi, '') + `-${author.id}`; + const channelName = author.tag.replace(/[^0-9a-z]/gi, "") + `-${author.id}`; const newChannel = await threadServer.channels.create(channelName, { type: "text" }); // Set channel parent and then set the permissions - await newChannel.setParent(categoryID).then(chnl => chnl.lockPermissions()) - const logEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("New Thread") - .setDescription(`${args.join(' ')}`) - .setFooter(`${author.tag} | ${author.id}`, author.avatarURL()) - .setTimestamp(); + await newChannel.setParent(categoryID).then(chnl => chnl.lockPermissions()); + + const logEmbed = getEmbed.execute(param, "", config.info_color, "New Thread", args.join(" "), "", author); logChannel.send(logEmbed); - const dmEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("Thread Created!") - .setDescription( - `**Title** : ${args.join(' ')}\n\`Please describe your issue. (No command needed.)\`` - ) - .setFooter(mainServer.name, mainServer.iconURL()) - .setTimestamp(); + + const dmEmbed = getEmbed.execute(param, "", config.info_color, "Thread Created!", `**Title** : ${args.join(" ")}\n\`Please describe your issue. (No command needed.)\``, "", mainServer); author.send(dmEmbed); + const member = await mainServer.members.cache.get(author.id); - const memberRoles = await member.roles.cache.filter(role => role.name != '@everyone').map(role => `\`${role.name}\``).join(', '); + const memberRoles = await member.roles.cache.filter(role => role.name != "@everyone").map(role => `<@&${role.id}>`).join(", "); const userData = []; - userData.push(`**User Tag** : \`${author.tag}\``); - userData.push(`**User ID** : \`${author.id}\``); - userData.push(`**Created at** : ${moment(author.createdAt).format("D MMM YYYY, HH:mm")}`); - userData.push(`**Joined at** : ${moment(member.joinedAt).format("D MMM YYYY, HH:mm")}`); - userData.push(`**Roles** : ${memberRoles}`); - const newThreadEmbed = new Discord.MessageEmbed() - .setColor(config.info_color) - .setTitle("New Thread") - .setDescription(args.join(' ')) - .addField("User Info", userData.join('\n')) - .setThumbnail(author.avatarURL()) - .setFooter(`${author.tag} | ${author.id}`, author.avatarURL()) - .setTimestamp(); + userData.push(`**Created at**: ${moment(author.createdAt).format("D MMM YYYY, HH:mm")}`); + userData.push(`**Joined at**: ${moment(member.joinedAt).format("D MMM YYYY, HH:mm")}`); + userData.push(`**Roles**: ${memberRoles}`); + const newThreadEmbed = getEmbed.execute(param, "", config.info_color, "New Thread", args.join(" "), [`User Info;${userData.join("\n")}`], author, author.displayAvatarURL()); newChannel.send(mentionedRole, newThreadEmbed); if (message.attachments.size > 0) { await message.attachments.forEach(async atch => { @@ -71,10 +56,8 @@ module.exports = { }); } - const newThread = await db.set(threadPrefix + author.id, `${newChannel.id}-${args.join(' ')}`); - console.log(`${author.tag}(${newThread.userID}) created thread.`); - + await db.set(threadPrefix + author.id, `${newChannel.id}-${args.join(" ")}`).then(() => console.log("> Thread Created!")); await updateActivity.execute(param); - } + }, }; diff --git a/functions/replyFn.js b/functions/replyFn.js deleted file mode 100644 index 5b4f1ad..0000000 --- a/functions/replyFn.js +++ /dev/null @@ -1,83 +0,0 @@ -module.exports = { - name: "reply", - async execute(param, message, args) { - const Discord = param.Discord; - const MessageAttachment = param.MessageAttachment; - const client = param.client; - const getEmbed = param.getEmbed; - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - const isMember = param.isMember; - const isBlocked = param.isBlocked; - - const mainServerID = config.mainServerID; - const mainServer = await client.guilds.cache.get(mainServerID); - const author = message.author; - const channel = message.channel; - - const userID = channel.name.split("-").pop(); - const isThread = await db.get(threadPrefix + userID); - const checkIsBlocked = await isBlocked.execute(param, author.id); - - const blockedEmbed = getEmbed.execute(param, config.error_color, "Blocked", `User blocked.`); - const noDMEmbed = getEmbed.execute(param, config.error_color, "Not Sent", `User disabled Direct Message.`); - const noUserEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find user in my collection.`); - const noThreadEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find any thread asociated with this channel.`); - - if (!isThread) { - return channel.send(noThreadEmbed); - } else { - const checkIsMember = await isMember.execute(param, userID); - const notMemberEmbed = getEmbed.execute(param, config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); - - if (!checkIsMember) { - return channel.send(notMemberEmbed); - } else if (checkIsBlocked) { - return channel.send(blockedEmbed); - } else { - const member = await mainServer.members.cache.get(userID); - - if (!member) { - return channel.send(noUserEmbed); - } else { - const user = member.user; - const description = args.join(' '); - const userDMEmbed = new Discord.MessageEmbed() - .setColor(config.sent_color) - .setAuthor(author.tag, author.avatarURL()) - .setTitle("Message Received") - .setDescription(description) - .setFooter(mainServer.name, mainServer.iconURL()) - .setTimestamp(); - const threadChannelEmbed = new Discord.MessageEmbed() - .setColor(config.sent_color) - .setAuthor(author.tag, author.avatarURL()) - .setTitle("Message Sent") - .setDescription(description) - .setFooter(`${user.tag} | ${user.id}`, user.avatarURL()) - .setTimestamp(); - - try{ - await user.send(userDMEmbed); - } catch (error) { - if(error.message == "Cannot send messages to this user") { - return channel.send(noDMEmbed); - } - } - await channel.send(threadChannelEmbed); - if (message.attachments.size > 0) { - await message.attachments.forEach(async atch => { - const attachment = new MessageAttachment(atch.url); - await user.send(attachment); - await channel.send(attachment); - }); - } - return message.delete(); - } - - } - } - - } -}; diff --git a/functions/resetFn.js b/functions/resetFn.js deleted file mode 100644 index 1587c87..0000000 --- a/functions/resetFn.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - name: "reset", - async execute(param) { - const defConfig = param.defConfig; - const db = param.db; - const configPrefix = param.dbPrefix.config; - const configKeys = Object.keys(param.config); - - configKeys.forEach(async aKey => { - const dbKey = configPrefix + aKey; - await db.set(dbKey, defConfig[aKey]); - }); - return param.configSync.execute(param); - } -}; diff --git a/functions/roleCheckFn.js b/functions/roleCheck.js similarity index 72% rename from functions/roleCheckFn.js rename to functions/roleCheck.js index 1668d8e..af83ccd 100644 --- a/functions/roleCheckFn.js +++ b/functions/roleCheck.js @@ -1,12 +1,15 @@ module.exports = { name: "roleCheck", async execute(message, roleID) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const isRole = message.member.roles.cache.get(roleID); if(isRole) { return true; - } else { + } + else { return false; } - } + }, }; diff --git a/functions/setFn.js b/functions/setFn.js deleted file mode 100644 index 84fe984..0000000 --- a/functions/setFn.js +++ /dev/null @@ -1,150 +0,0 @@ -module.exports = { - name: "set", - async execute(param, message, args) { - const client = param.client; - const config = param.config; - const getEmbed = param.getEmbed; - const configSync = param.configSync; - const db = param.db; - const configPrefix = param.dbPrefix.config; - const configName = args.shift(); - const dbKey = configPrefix + configName; - let inputValue = args.shift() || "empty"; - - // manual toggle since i set database to String, can't store boolean for maintenance config. - if(configName == "maintenance") { - if(config.maintenance == "0") { - inputValue = "1"; - } else { - inputValue = "0"; - } - } - - const notOwnerEmbed = getEmbed.execute(param, config.warning_color, "Missing Permission", `Only bot owner [<@${config.botOwnerID}>] can change this value.`); - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `The value of \`${configName}\` changed to \`${inputValue}\``); - const notSetEmbed = getEmbed.execute(param, config.warning_color, "Configuration Needed", `Please set \`threadServerID\` to change this config.`); - const emptyValueEmbed = getEmbed.execute(param, config.warning_color, "Invalid Value", "This config value can't be empty."); - const invalidUserEmbed = getEmbed.execute(param, config.warning_color, "Invalid User", `Can't find that user.`); - const notNumberEmbed = getEmbed.execute(param, config.warning_color, "Invalid Argument", `That isn't a number.`); - const negativeNumberEmbed = getEmbed.execute(param, config.warning_color, "Invalid Argument", `The value can't be negative.`); - const invalidServerEmbed = getEmbed.execute(param, config.warning_color, "Invalid Server", `Can't find that server.\n\`Make sure the bot joined the server already\``); - const invalidChannelMainEmbed = getEmbed.execute(param, config.warning_color, "Invalid Channel", `Can't find that channel.\n\`Make sure the channel is inside Main Server.\``); - const invalidChannelThreadEmbed = getEmbed.execute(param, config.warning_color, "Invalid Channel", `Can't find that channel.\n\`Make sure the channel is inside Thread Server.\``); - const invalidCategoryEmbed = getEmbed.execute(param, config.warning_color, "Invalid Category", `That isn't a category channel.`); - const invalidTextChannelEmbed = getEmbed.execute(param, config.warning_color, "Invalid Channel", `That isn't a text channel.`); - const invalidRoleEmbed = getEmbed.execute(param, config.warning_color, "Invalid Role", `Can't find that role.\n\`Make sure the role is inside Thread Server.\``); - const invalidColorEmbed = getEmbed.execute(param, config.warning_color, "Invalid Color", `Use hex code for input.\nCheck : `); - - // elimination for invalid input - if(configName == "botOwnerID") { - - if(message.author.id != config.botOwnerID) { - return message.channel.send(notOwnerEmbed); - } else if(inputValue == "empty") { - return message.channel.send(emptyValueEmbed); - } else if(!client.users.cache.get(inputValue)) { - return message.channel.send(invalidUserEmbed); - } - - } else if(configName == "cooldown") { - - if (isNaN(inputValue)) { - return message.channel.send(notNumberEmbed); - } else if(inputValue < 0) { - return message.channel.send(negativeNumberEmbed); - } - - } else if(configName == "mainServerID" && inputValue != "empty") { - - if(!client.guilds.cache.get(inputValue)) { - return message.channel.send(invalidServerEmbed); - } - - } else if(configName == "threadServerID" && inputValue != "empty") { - - if(!client.guilds.cache.get(inputValue)) { - return message.channel.send(invalidServerEmbed); - } - - } else if(configName == "categoryID" && inputValue != "empty") { - - const getChannel = client.guilds.cache.get(config.threadServerID).channels.cache.get(inputValue); - if(config.threadServerID == "empty") { - return message.channel.send(notSetEmbed); - } else if(!getChannel) { - return message.channel.send(invalidChannelThreadEmbed); - } else if(getChannel.type != "category") { - return message.channel.send(invalidCategoryEmbed); - } - - } else if(configName == "logChannelID" && inputValue != "empty") { - - const getChannel = client.guilds.cache.get(config.threadServerID).channels.cache.get(inputValue); - if(config.threadServerID == "empty") { - return message.channel.send(notSetEmbed); - } else if(!getChannel) { - return message.channel.send(invalidChannelThreadEmbed); - } else if(getChannel.type != "text") { - return message.channel.send(invalidTextChannelEmbed); - } - - } else if(configName == "botChannelID" && inputValue != "empty") { - - const getChannel = client.guilds.cache.get(config.mainServerID).channels.cache.get(inputValue); - if(config.mainServerID == "empty") { - return message.channel.send(notSetEmbed); - } else if(!getChannel) { - return message.channel.send(invalidChannelMainEmbed); - } else if(getChannel.type != "text") { - return message.channel.send(invalidTextChannelEmbed); - } - - } else if((configName == "adminRoleID" || configName == "modRoleID") && inputValue != "empty") { - - if(config.threadServerID == "empty") { - return message.channel.send(notSetEmbed); - } else if(!client.guilds.cache.get(config.threadServerID).roles.cache.get(inputValue)) { - return message.channel.send(invalidRoleEmbed); - } - - } else if(configName == "mentionedRoleID" && inputValue != "empty" && inputValue != "everyone" && inputValue != "here") { - - if(config.threadServerID == "empty") { - return message.channel.send(notSetEmbed); - } else if(!client.guilds.cache.get(config.threadServerID).roles.cache.get(inputValue)) { - return message.channel.send(invalidRoleEmbed); - } - - } else if(configName == "info_color" || configName == "warning_color" || configName == "error_color" || configName == "sent_color" || configName == "received_color") { - - const colorTest = /^#[0-9A-F]{6}$/i; - if(inputValue == "empty") { - return message.channel.send(emptyValueEmbed); - } else if(colorTest.test(inputValue) == false) { - return message.channel.send(invalidColorEmbed); - } - - } - - // getting all the config name from Database - const configCollection = await db.list(configPrefix); - const configList = configCollection.map(conf => `\`${conf.slice(configPrefix.length)}\``).join(', '); - - if(configCollection.includes(dbKey)) { - await db.set(dbKey, inputValue); - console.log(`[${dbKey}] value changed to [${inputValue}]`); - await message.channel.send(successEmbed).then(async () => { - await configSync.execute(param); - if(configName == "maintenance" || configName == "prefix") { - setTimeout(async ()=> { - await param.updateActivity.execute(param); - }, 5000); - } - }); - } else { - const notFoundEmbed = getEmbed.execute(param, config.error_color, "Failed", `Can't find config named \`${configName}\`.\nAvailable names : ${configList}`); - return message.channel.send(notFoundEmbed) - } - - } -}; diff --git a/functions/tagFn.js b/functions/tagFn.js deleted file mode 100644 index f05defd..0000000 --- a/functions/tagFn.js +++ /dev/null @@ -1,110 +0,0 @@ -module.exports = { - name: "tag", - async execute(param, message, args) { - const Discord = param.Discord; - const client = param.client; - const config = param.config; - const db = param.db; - const tagPrefix = param.dbPrefix.tag; - const threadPrefix = param.dbPrefix.thread; - const getEmbed = param.getEmbed; - - const mainServerID = config.mainServerID; - const mainServer = await client.guilds.cache.get(mainServerID); - const author = message.author; - const channel = message.channel; - const tagName = args.join(' ').toLowerCase(); - const dbKey = tagPrefix + tagName; - - const isTag = await db.get(dbKey); - const userID = channel.name.split("-").pop(); - const isThread = await db.get(threadPrefix + userID); - const tagCollection = await db.list(tagPrefix); - const tagList = tagCollection.map(tag => `\`${tag.slice(tagPrefix.length)}\``).join(', ') || "No available tag"; - - const noTagEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); - const cancelEmbed = getEmbed.execute(param, config.error_color, "Canceled", `Command are canceled.`); - const timeoutEmbed = getEmbed.execute(param, config.error_color, "Timeout", `Timeout, command are canceled.`); - const noDMEmbed = getEmbed.execute(param, config.error_color, "Not Sent", `User disabled Direct Message.`); - - if(!isTag) { - // can't find tag - console.log(`Tag not found.`); - return message.channel.send(noTagEmbed); - } else if(!isThread) { - // no user thread - console.log(`Not a thread channel.`); - const noThreadEmbed = getEmbed.execute(param, config.info_color, "", isTag); - return message.channel.send(noThreadEmbed).then(message.delete()); - } else { - // There's user thread and tag - console.log(`Thread channel.`); - const checkIsBlocked = await param.isBlocked.execute(param, userID); - const checkIsMember = await param.isMember.execute(param, author.id); - const blockedEmbed = getEmbed.execute(param, config.error_color, "Blocked", `User blocked.`); - const notMemberEmbed = getEmbed.execute(param, config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); - - const filter = (reaction, user) => { - return user.id === message.author.id && (reaction.emoji.name == '✅' || reaction.emoji.name == '❌'); - } - - const waitingEmbed = new param.Discord.MessageEmbed() - .setColor(config.info_color) - .setDescription(isTag + `\n\nReact with ✅ to send, ❌ to cancel.\n\`Timeout: 30 seconds.\``) - .setFooter(param.client.user.tag, param.client.user.avatarURL()) - .setTimestamp(); - - const botMsg = await message.channel.send(waitingEmbed); - await botMsg.react('✅'); - await botMsg.react('❌'); - - botMsg.awaitReactions(filter, { max: 1, time: 30000, errors: ['time'] }) - .then(async collected => { - if (collected.first().emoji.name == '❌') { - // user react with ❌ - await botMsg.reactions.removeAll(); - return message.channel.send(cancelEmbed); - } else if (!checkIsMember) { - // user react with ✅ - // the user that has thread not in main server - await botMsg.reactions.removeAll(); - return message.channel.send(notMemberEmbed); - } else if(checkIsBlocked) { - // the user that has thread are blocked - await botMsg.reactions.removeAll(); - return message.channel.send(blockedEmbed); - } else { - // user are member and not blocked - const getUser = await mainServer.members.cache.get(userID).user; - const userDMEmbed = new Discord.MessageEmbed() - .setColor(config.sent_color) - .setTitle("Message Received") - .setDescription(isTag) - .setFooter(mainServer.name, mainServer.iconURL()) - .setTimestamp(); - const threadChannelEmbed = new Discord.MessageEmbed() - .setColor(config.sent_color) - .setAuthor(`[Anonymous] | ${author.tag}`, author.avatarURL()) - .setTitle("Message Sent") - .setDescription(isTag) - .setFooter(`${getUser.tag} | ${getUser.id}`, getUser.avatarURL()) - .setTimestamp(); - - try{ - await getUser.send(userDMEmbed); - } catch (error) { - if(error.message == "Cannot send messages to this user") { - await botMsg.reactions.removeAll(); - return channel.send(noDMEmbed); - } - } - await channel.send(threadChannelEmbed); - return message.delete().then(botMsg.delete()); - } - }) - .catch(collected => { - return message.channel.send(timeoutEmbed); - }); - } - } -}; diff --git a/functions/tagaddFn.js b/functions/tagaddFn.js deleted file mode 100644 index b790ff8..0000000 --- a/functions/tagaddFn.js +++ /dev/null @@ -1,46 +0,0 @@ -module.exports = { - name: "tagadd", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const tagPrefix = param.dbPrefix.tag; - const getEmbed = param.getEmbed; - - const tagName = args.join(' ').toLowerCase(); - - const duplicatedEmbed = getEmbed.execute(param, config.error_color, "Duplicated", `There's a tag named (\`${tagName}\`) already.`); - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Succesfully add (\`${tagName}\`) tag.`); - const waitingEmbed = getEmbed.execute(param, config.info_color, "Response", `Please write the response for this tag.\nType \`cancel\` to cancel the command.\n\n\`Timeout: 30 seconds.\``); - const cancelEmbed = getEmbed.execute(param, config.error_color, "Canceled", `Command are canceled.`); - const timeoutEmbed = getEmbed.execute(param, config.error_color, "Timeout", `Timeout, command are canceled.`); - - const dbKey = tagPrefix + tagName; - const isDuplicated = await db.get(dbKey); - if(isDuplicated) { - console.log(`Duplicated tag name.`); - return message.channel.send(duplicatedEmbed); - } else { - const filter = msg => msg.author.id == message.author.id; - - message.channel.send(waitingEmbed).then(() => { - - message.channel.awaitMessages(filter, { max: 1, time: 30000, errors: ['time'] }) - .then(async collected => { - if (collected.first().content.toLowerCase() == "cancel") { - return message.channel.send(cancelEmbed); - } else { - const content = collected.first().content; - db.set(dbKey, content).then(() => { - console.log(`Added [${tagName}] tag.`); - return message.channel.send(successEmbed); - }); - } - }) - .catch(collected => { - return message.channel.send(timeoutEmbed); - }); - - }); - } - } -}; diff --git a/functions/tagdeleteFn.js b/functions/tagdeleteFn.js deleted file mode 100644 index 5928d89..0000000 --- a/functions/tagdeleteFn.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - name: "tagdelete", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const tagPrefix = param.dbPrefix.tag; - const getEmbed = param.getEmbed; - - const tagName = args.join(' ').toLowerCase() - const dbKey = tagPrefix + tagName; - const isTag = await db.get(dbKey); - const tagCollection = await db.list(tagPrefix); - const tagList = tagCollection.map(tag => `\`${tag.slice(tagPrefix.length)}\``).join(', ') || "No available tag"; - - const noTagEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Deleted (\`${tagName}\`) tag.`); - - if(!isTag) { - console.log(`Tag not found.`); - return message.channel.send(noTagEmbed); - } else { - db.delete(dbKey).then(() => { - console.log(`Deleted ${tagName}.`); - return message.channel.send(successEmbed); - }); - } - - } -}; diff --git a/functions/tageditFn.js b/functions/tageditFn.js deleted file mode 100644 index a3b4e82..0000000 --- a/functions/tageditFn.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = { - name: "tagedit", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const tagPrefix = param.dbPrefix.tag; - const getEmbed = param.getEmbed; - - const tagName = args.join(' ').toLowerCase(); - const dbKey = tagPrefix + tagName; - const isTag = await db.get(dbKey); - const tagCollection = await db.list(tagPrefix); - const tagList = tagCollection.map(tag => `\`${tag.slice(tagPrefix.length)}\``).join(', ') || "No available tag"; - - const noTagEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Succesfully edit (\`${tagName}\`) tag response.`); - const waitingEmbed = getEmbed.execute(param, config.info_color, "Response", `Please write new response for this tag.\nType \`cancel\` to cancel the command.\n\n\`Timeout: 30 seconds.\``); - const cancelEmbed = getEmbed.execute(param, config.error_color, "Canceled", `Command are canceled.`); - const timeoutEmbed = getEmbed.execute(param, config.error_color, "Timeout", `Timeout, command are canceled.`); - - if(!isTag) { - console.log(`Tag not found.`); - return message.channel.send(noTagEmbed); - } else { - const filter = msg => msg.author.id == message.author.id; - - message.channel.send(waitingEmbed).then(() => { - message.channel.awaitMessages(filter, { max: 1, time: 30000, errors: ['time'] }) - .then(async collected => { - if (collected.first().content.toLowerCase() == "cancel") { - return message.channel.send(cancelEmbed); - } else { - const content = collected.first().content; - db.set(dbKey, content).then(() => { - console.log(`Edited [${tagName}] tag`); - return message.channel.send(successEmbed); - }); - } - }) - .catch(collected => { - return message.channel.send(timeoutEmbed); - }); - }); - } - - } -}; diff --git a/functions/taginfoFn.js b/functions/taginfoFn.js deleted file mode 100644 index f2ddfd0..0000000 --- a/functions/taginfoFn.js +++ /dev/null @@ -1,32 +0,0 @@ -module.exports = { - name: "taginfo", - async execute(param, message, args) { - const moment = param.moment; - const config = param.config; - const db = param.db; - const tagPrefix = param.dbPrefix.tag; - const getEmbed = param.getEmbed; - - const tagName = args.join(' ').toLowerCase(); - const dbKey = tagPrefix + tagName; - const isTag = await db.get(dbKey); - const tagCollection = await db.list(tagPrefix); - const tagList = tagCollection.map(tag => `\`${tag.slice(tagPrefix.length)}\``).join(', ') || "No available tag"; - - const noTagEmbed = getEmbed.execute(param, config.error_color, "Not Found", `CouldnS't find tag named \`${tagName}\`.\nAvailable names : ${tagList}`); - - if(!isTag) { - console.log(`Tag not found.`); - return message.channel.send(noTagEmbed); - } else { - const data = []; - - data.push(`**Name** : ${tagName}`); - data.push(`**Response** : \`\`\`${isTag}\`\`\``); - - const tagInfoEmbed = getEmbed.execute(param, config.info_color, "Tag Information", data.join('\n')); - return message.channel.send(tagInfoEmbed); - } - - } -}; diff --git a/functions/threadinfoFn.js b/functions/threadinfoFn.js deleted file mode 100644 index f693c86..0000000 --- a/functions/threadinfoFn.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = { - name: "threadinfo", - async execute(param, message, args) { - const moment = param.moment; - const client = param.client; - const getEmbed = param.getEmbed; - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - - const mainServerID = config.mainServerID; - const mainServer = await client.guilds.cache.get(mainServerID); - - const userID = args.shift(); - const dbKey = threadPrefix + userID; - const isThread = await db.get(dbKey); - - const noThreadEmbed = getEmbed.execute(param, config.error_color, "Not Found", `Couldn't find any thread asociated with that user id.`); - - if (!isThread) { - return message.channel.send(noThreadEmbed); - } else { - const threadData = []; - const member = mainServer.members.cache.get(userID); - - const temp = isThread.split("-"); - const channelID = temp.shift(); - threadData.push(`${temp.join("-")}`); - if (member) { - threadData.push(`**User Tag** : \`${member.user.tag}\``); - } else { - threadData.push(`**User Tag** : \`Couldn't find user at main server.\``); - } - threadData.push(`**User ID** : \`${userID}\``); - threadData.push(`**Thread Channel** : <#${channelID}>`); - - const threadInfoEmbed = getEmbed.execute(param, config.info_color, "Thread Information", threadData.join('\n')); - return message.channel.send(threadInfoEmbed); - } - - } -}; diff --git a/functions/threadlistFn.js b/functions/threadlistFn.js deleted file mode 100644 index be9b2de..0000000 --- a/functions/threadlistFn.js +++ /dev/null @@ -1,43 +0,0 @@ -module.exports = { - name: "threadlist", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const threadPrefix = param.dbPrefix.thread; - const getEmbed = param.getEmbed; - - let pageNumber = args.shift(); - - const threadlist = await db.list(threadPrefix); - let pages = Math.floor(threadlist.length / 20); - if (threadlist.length % 20 != 0) { - // add 1 number of pages if residual quotient is not 0 (15%10=5 -> 5 > 0) - pages += 1; - } - if (threadlist.length == 0) { - pageNumber = 0; - } else if(!pageNumber || isNaN(pageNumber) || pageNumber <= 0) { - // user didn't gave input or input is not a number or input is below or same as 0 - pageNumber = 1; - } else if(pageNumber > pages) { - // input is higher than the number of pages - pageNumber = pages; - } - - const listArray = threadlist.map(key => { - const cleanKey = key.slice(threadPrefix.length); - return `🔹 <@${cleanKey}> (\`${cleanKey}\`)` - }); - const firstIndex = Math.abs((pageNumber - 1) * 20); - let listString = listArray.slice(firstIndex, firstIndex + 20).join("\n") || `\`List empty.\``; - if (pages > 1) { - listString += `\n\`Page ${pageNumber} from ${pages} pages\``; - } else { - listString += `\n\`Page ${pageNumber} from ${pages} page\``; - } - - const listEmbed = getEmbed.execute(param, config.info_color, "Open Threads", listString); - return message.channel.send(listEmbed); - - } -}; diff --git a/functions/unblockFn.js b/functions/unblockFn.js deleted file mode 100644 index 1544b78..0000000 --- a/functions/unblockFn.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - name: "unblock", - async execute(param, message, args) { - const config = param.config; - const db = param.db; - const blockPrefix = param.dbPrefix.block; - const getEmbed = param.getEmbed; - - const userID = args.shift(); - - const notFoundEmbed = getEmbed.execute(param, config.error_color, "Not Found", `<@${userID}> (\`${userID}\`) isn't blocked.`); - const successEmbed = getEmbed.execute(param, config.info_color, "Success", `Succesfully unblock <@${userID}> (\`${userID}\`).`); - - const dbKey = blockPrefix + userID; - const blockData = await db.get(dbKey); - if(blockData) { - db.delete(dbKey).then(() => { - console.log(`Unblocked ${userID}.`); - return message.channel.send(successEmbed); - }) - } else { - return message.channel.send(notFoundEmbed); - } - } -}; diff --git a/functions/updateActivity.js b/functions/updateActivity.js index 444a48a..5f3af25 100644 --- a/functions/updateActivity.js +++ b/functions/updateActivity.js @@ -1,6 +1,8 @@ module.exports = { name: "updateActivity", async execute(param) { + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const client = param.client; const config = param.config; const prefix = config.prefix; @@ -8,7 +10,7 @@ module.exports = { const db = param.db; const threadPrefix = param.dbPrefix.thread; - const threads = await db.list(threadPrefix) + const threads = await db.list(threadPrefix); const threadServer = client.guilds.cache.get(config.threadServerID); let maxThreads = ""; if (threadServer) { @@ -17,10 +19,12 @@ module.exports = { const childSize = categoryChannel.children.size; const threadSize = threads.length; maxThreads = 50 - (childSize - threadSize); - } else { + } + else { maxThreads = 0; } - } else { + } + else { maxThreads = 0; } const activities = [ @@ -34,16 +38,17 @@ module.exports = { `Пришлите мне сообщение | ${prefix}helpRU`, `给我发一条信息 | ${prefix}helpCHS`, `給我發一條信息 | ${prefix}helpCHT`, - `Envíeme un mensaje | ${prefix}helpES` - ] + `Envíeme un mensaje | ${prefix}helpES`, + ]; if (config.maintenance == 0) { client.user.setActivity(activities[activity.index]); activity.index++; if (activity.index == activities.length) activity.index = 0; - } else { + } + else { client.user.setActivity("~ Under Maintenance ~"); } - console.log(`> Activity Updated. Index : ${activity.index}`) - } + console.log(`> Activity Updated. Index : ${activity.index}`); + }, }; diff --git a/functions/userReply.js b/functions/userReply.js index f0f8687..25b48bf 100644 --- a/functions/userReply.js +++ b/functions/userReply.js @@ -1,7 +1,8 @@ module.exports = { name: "userReply", async execute(param, message, thread) { - const Discord = param.Discord; + console.log(`~~ ${this.name.toUpperCase()} ~~`); + const MessageAttachment = param.MessageAttachment; const client = param.client; const getEmbed = param.getEmbed; @@ -17,52 +18,49 @@ module.exports = { const checkIsBlocked = await isBlocked.execute(param, author.id); - const blockedEmbed = getEmbed.execute(param, config.error_color, "Blocked", `You are blocked from replying to a thread.`); - const noServerEmbed = getEmbed.execute(param, config.error_color, "Contact Admin", "`mainServerID` and/or `threadServerID` value is empty."); - const noChannelEmbed = getEmbed.execute(param, config.error_color, "Channel Not Found", `Couldn't find your thread channel, ask admin to use \`${config.prefix}bind\` command.`); + const blockedEmbed = getEmbed.execute(param, "", config.error_color, "Blocked", "You are blocked from replying to a thread."); + const noServerEmbed = getEmbed.execute(param, "", config.error_color, "Contact Admin", "`mainServerID` and/or `threadServerID` value is invalid."); + const noChannelEmbed = getEmbed.execute(param, "", config.error_color, "Channel Not Found", `Couldn't find your thread channel, ask admin to use \`${config.prefix}bind\` command.`); + const userReplyEmbed = getEmbed.execute(param, "", config.received_color, "Message Received", message.content, "", author); + + const threadChannel = threadServer ? await threadServer.channels.cache.get(thread.channelID) : false; + async function send() { + await threadChannel.send(userReplyEmbed); + + if (message.attachments.size > 0) { + for(const atch in message.attachments) { + const attachment = new MessageAttachment(atch.url); + await threadChannel.send(attachment); + } + } + } if(checkIsBlocked) { // User is blocked + console.log("> User are blocked."); return message.channel.send(blockedEmbed); - } else if (!mainServer || !threadServer) { + } + else if (!mainServer || !threadServer) { // Can't find main server or thread server + console.log("> Can't find main or thread server."); return message.channel.send(noServerEmbed); - } else { + } + else { const checkIsMember = await isMember.execute(param, author.id); - const notMemberEmbed = getEmbed.execute(param, config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); - const threadChannel = await threadServer.channels.cache.get(thread.channelID); + const notMemberEmbed = getEmbed.execute(param, "", config.error_color, "Not a Member", `User aren't inside [**${mainServer.name}**] guild.`); if(!checkIsMember) { + console.log("> User aren't a member of main server."); return message.channel.send(notMemberEmbed); - } else if(!threadChannel) { + } + else if(!threadChannel) { + console.log("> Can't find this user's thread channel."); return message.channel.send(noChannelEmbed); - } else { - const sendPromise = new Promise(resolve => { - - async function send() { - const userReplyEmbed = new Discord.MessageEmbed() - .setColor(config.received_color) - .setTitle("Message Received") - .setDescription(message.content) - .setFooter(`${author.tag} | ${author.id}`, author.avatarURL()) - .setTimestamp(); - await threadChannel.send(userReplyEmbed); - - if (message.attachments.size > 0) { - await message.attachments.forEach(async atch => { - const attachment = new MessageAttachment(atch.url); - await threadChannel.send(attachment); - }); - } - resolve(); - } - send(); - - }); - sendPromise.then(message.react("✅")); - + } + else { + send().then(message.react("✅")); } } - } + }, }; diff --git a/index.js b/index.js index c6ae716..0b5bd42 100644 --- a/index.js +++ b/index.js @@ -1,333 +1,145 @@ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DEPENDENCIES~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// #dependencies -console.log("[Loading Dependencies]"); -const Discord = require("discord.js"); -const { Util, MessageAttachment } = require("discord.js"); -const client = new Discord.Client(); -const keepAlive = require("./server.js"); -const fs = require("fs"); -const moment = require("moment"); -const defConfig = require("./config.json"); -const Database = require("@replit/database"); -const db = new Database(); -require("dotenv").config(); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~VARIABLES~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -console.log("[Loading Variables]"); - -// #config (synced with database) -const config = { - prefix: "", - botOwnerID: "", - cooldown: "", - maintenance: "", - mainServerID: "", - threadServerID: "", - categoryID: "", - logChannelID: "", - botChannelID: "", - adminRoleID: "", - modRoleID: "", - mentionedRoleID: "", - info_color: "", - warning_color: "", - error_color: "", - received_color: "", - sent_color: "" -}; - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DATABASE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const dbPrefix = { - block: "bck.", - config: "cfg.", - thread: "trd.", - tag: "tag.", -} - -/* Database Structure - ~ Block ~ - bck.userID = modID-reason - ~ Config~ - cfg.configName = value - ~ Thread ~ - trd.userID = channelID-threadTitle - ~ Tag ~ - tag.tagName = content -*/ - -// For debugging purpose, deleting all keys in the database. -// db.list().then(keys => keys.forEach(async key => {await db.delete(key)})); -// db.list(dbPrefix.block).then(keys => keys.forEach(async key => {await db.delete(key)})); -// db.list(dbPrefix.config).then(keys => keys.forEach(async key => {await db.delete(key)})); -// db.list(dbPrefix.thread).then(keys => keys.forEach(async key => {await db.delete(key)})); -// db.list(dbPrefix.tag).then(keys => keys.forEach(async key => {await db.delete(key)})); - -// #commandsCollection -client.commands = new Discord.Collection(); -const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js')); -for (const file of commandFiles) { - const cmd = require(`./commands/${file}`); - client.commands.set(cmd.name, cmd); -} - -// #functionsCollection -client.functions = new Discord.Collection(); -const functionFiles = fs.readdirSync('./functions').filter(file => file.endsWith('.js')); -for (const file of functionFiles) { - const func = require(`./functions/${file}`); - client.functions.set(func.name, func); -} - -// #cooldownCollection -const cooldowns = new Discord.Collection(); - -// #activities -const activity = { - index: 0 -}; - -// #parameter -const param = { - Discord, - MessageAttachment, - moment, - client, - db, - dbPrefix, - config, - defConfig, - activity, -} -// add every functions to param object -client.functions.forEach(fn => param[fn.name] = client.functions.get(fn.name)); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~READY~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -client.on('ready', async () => { - console.log("[Syncing Database]"); - // cant make the bot waiting this guy below to finish pfft - await param.configSync.execute(param); - - console.log(`Logged in as ${client.user.tag}!`); - setTimeout(async ()=> { - await param.updateActivity.execute(param); - }, 5000); -}); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~NEW GUILD~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -client.on('guildCreate', async guild => { - const ownerID = config.botOwnerID; - if(ownerID) { - const newServerEmbed = param.getEmbed.execute(param, config.info_color, "Joined a Guild", `[**${guild.name}**] (\`${guild.id}\`)`); - client.users.cache.get(ownerID).send(newServerEmbed); - } - console.log(`Joined [${guild.name}] guild.`); -}); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~MESSSAGES~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -const escapeRegex = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - -client.on('message', async message => { - if(message.author.bot) return; - let now = Date.now(); - let timestamps, cooldownAmount; - - // Activity Cooldown - if (!cooldowns.has("botActivity")) { - cooldowns.set("botActivity", new Discord.Collection()); - } - now = Date.now(); - timestamps = cooldowns.get("botActivity"); - // 7 Seconds - cooldownAmount = 7000; - if (!timestamps.has("botActivity")) { - await param.updateActivity.execute(param); - } - timestamps.set("botActivity", now); - setTimeout(() => timestamps.delete("botActivity"), cooldownAmount); - - try { - const authorID = message.author.id; - const maintenanceEmbed = param.getEmbed.execute(param, config.error_color, "Maintenance", "All functions are disabled."); - - let args, commandName = ""; - - // checking whether user use prefix or mention the bot - const prefixRegex = new RegExp(`^(<@!?${client.user.id}>|${escapeRegex(config.prefix)})\\s*`); - if (!prefixRegex.test(message.content)) { - // user didn't use prefix or mention the bot - if(message.guild != null) { - // message are inside a guild - return; - } else { - // Direct Message - const isThread = await db.get(dbPrefix.thread + message.author.id); - if(!isThread) { - // User didn't have any open thread - return; - } else if(config.maintenance == "1" && authorID != config.botOwnerID) { - // Maintenance mode enabled - return message.channel.send(maintenanceEmbed); - } else { - // User have open thread and maintenance mode disabled - const temp = isThread.split("-"); - const thread = { - channelID: temp.shift(), - threadTitle: temp.join("-") - } - return param.userReply.execute(param, message, thread); - } - } - } else { - const [, matchedPrefix] = message.content.match(prefixRegex); - args = message.content.slice(matchedPrefix.length).trim().split(/ +/); - commandName = args.shift().toLowerCase(); - } - // finding command that was triggered - const command = client.commands.get(commandName) || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName)); - - // user using prefix or mention bot, command name is invalid - if (!command) return; - console.log(`${message.author.tag}(${message.author.id}) called ${commandName} command.`); - - // cooldown more than 0 - if (config.cooldown > 0) { - if (message.guild != null) { - if(config.botChannelID != "empty" && message.channel.id != config.botChannelID && message.guild.id == config.mainServerID && message.guild.id != config.threadServerID) { - return; - } else { - if (!cooldowns.has(command.name)) { - cooldowns.set(command.name, new Discord.Collection()); - } - - now = Date.now(); - timestamps = cooldowns.get(command.name); - cooldownAmount = config.cooldown * 1000; - - if (timestamps.has(message.author.id)) { - const expirationTime = timestamps.get(message.author.id) + cooldownAmount; - - if (now < expirationTime) { - // Uncomment this if you want the bot send message when the cooldown isn\'t over - /* - const timeLeft = (expirationTime - now) / 1000; - const embed = param.getEmbed.execute(param, config.info_color, "Cooldown", `${timeLeft.toFixed(1)} second(s).`) - return message.reply(embed); - */ - return; - } - } - - timestamps.set(message.author.id, now); - setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); - } - } else { - if (!cooldowns.has(command.name)) { - cooldowns.set(command.name, new Discord.Collection()); - } - - now = Date.now(); - timestamps = cooldowns.get(command.name); - cooldownAmount = config.cooldown * 1000; - - if (timestamps.has(message.author.id)) { - const expirationTime = timestamps.get(message.author.id) + cooldownAmount; - - if (now < expirationTime) { - // Uncomment this if you want the bot send message when the cooldown isn\'t over - /* - const timeLeft = (expirationTime - now) / 1000; - const embed = param.getEmbed.execute(param, config.info_color, "Cooldown", `${timeLeft.toFixed(1)} second(s).`) - return message.reply(embed); - */ - return; - } - } - - timestamps.set(message.author.id, now); - setTimeout(() => timestamps.delete(message.author.id), cooldownAmount); - } - } - - // maintenance mode - if(config.maintenance == "1") { - if(message.guild == null && authorID != config.botOwnerID) { - // in Direct Message and not bot owner - return message.channel.send(maintenanceEmbed); - } else if(config.mainServerID == "empty" && config.threadServerID == "empty") { - // the mainServerID and threadServerID empty - if(authorID != config.botOwnerID && !message.member.hasPermission('ADMINISTRATOR')) { - // not bot owner and doesn't have ADMINISTRATOR permission - return message.channel.send(maintenanceEmbed); - } - } else if(config.mainServerID != "empty" && config.threadServerID != "empty") { - // mainServerID and threadServerID isn't empty - if(message.guild.id == config.mainServerID || message.guild.id == config.threadServerID) { - // inside mainServerID or threadServerID - if(authorID != config.botOwnerID && !message.member.hasPermission('ADMINISTRATOR') && !(await param.roleCheck.execute(message, config.adminRoleID))) { - // not bot owner and user doesn't have ADMINISTRATOR permission nor have Admin role - if(config.botChannelID != "empty" && message.channel.id != config.botChannelID) { - return; - } else { - return message.channel.send(maintenanceEmbed); - } - } - } else if(authorID != config.botOwnerID) { - // outside mainServerID and threadServerID - // not bot owner - return message.channel.send(maintenanceEmbed); - } - } - } - - // command is guildOnly, user trigger it inside Direct Message - if(command.guildOnly && message.channel.type !== 'text' && message.author.id != config.botOwnerID) { - const noDMEmbed = param.getEmbed.execute(param, config.error_color, "Command Unavailable", "This command can't be used inside Direct Message."); - return message.channel.send(noDMEmbed); - } - - // command need arguments to run, user did't gave any - if(command.args && !args.length) { - let description = "You didn't provide any arguments." - - if(command.usage) { - description += `\n**Usage** : \`${config.prefix}${command.name} ${command.usage}\``; - } - if(command.note) { - description += `\n**Note** : \`${command.note}\``; - } - - const noArgsEmbed = param.getEmbed.execute(param, config.warning_color, "Missing Arguments", description); - return message.channel.send(noArgsEmbed); - } - - // command handler - // trying to execute the command - await command.execute(param, message, args); - } catch (error) { - // catching error -> log it in console -> send error message to user - console.log(error); - const errorEmbed = param.getEmbed.execute(param, config.error_color, "An Error Occured.", `**Contact bot Owner** : <@${config.botOwnerID}>\n**Error Name** : \`${error.name}\`\n**Error Message** : \`${error.message}\``); - return message.channel.send(errorEmbed); - } - -}); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~LOGIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -keepAlive(); -client.login(process.env.TOKEN); -require("https").createServer().listen(); - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DEPENDENCIES~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// #dependencies +console.log("[Loading Dependencies]"); +const Discord = require("discord.js"); +const { MessageAttachment } = require("discord.js"); +const client = new Discord.Client(); +const keepAlive = require("./server.js"); +const fs = require("fs"); +const process = require("process"); +const moment = require("moment"); +const defConfig = require("./config.json"); +const Database = require("@replit/database"); +const db = new Database(); +require("dotenv").config(); + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~DATABASE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// Database prefix, needed since replitDB save database in one file. +const dbPrefix = { + block: "bck.", + config: "cfg.", + thread: "trd.", + tag: "tag.", +}; + +/* Database Structure + ~ Block ~ + bck.userID = modID-reason + ~ Config~ + cfg.configName = value + ~ Thread ~ + trd.userID = channelID-threadTitle + ~ Tag ~ + tag.tagName = content +*/ + +// For debugging purpose, deleting all keys in the database. +// db.list().then(keys => keys.forEach(async key => {await db.delete(key)})); +// db.list(dbPrefix.block).then(keys => keys.forEach(async key => {await db.delete(key)})); +// db.list(dbPrefix.config).then(keys => keys.forEach(async key => {await db.delete(key)})); +// db.list(dbPrefix.thread).then(keys => keys.forEach(async key => {await db.delete(key)})); +// db.list(dbPrefix.tag).then(keys => keys.forEach(async key => {await db.delete(key)})); + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~VARIABLES~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +console.log("[Loading Variables]"); + +// #config (synced with database, saved in memory so the bot can access it faster) +const config = { + prefix: "", + botOwnerID: "", + cooldown: "", + maintenance: "", + mainServerID: "", + threadServerID: "", + categoryID: "", + logChannelID: "", + botChannelID: "", + adminRoleID: "", + modRoleID: "", + mentionedRoleID: "", + info_color: "", + warning_color: "", + error_color: "", + received_color: "", + sent_color: "", +}; + +// #activities +const activity = { + index: 0, +}; + +// #commandsCollection save commands in memory +client.commands = new Discord.Collection(); +const commandFiles = fs.readdirSync("./commands").filter(file => file.endsWith(".js")); +for (const file of commandFiles) { + const cmd = require(`./commands/${file}`); + client.commands.set(cmd.name, cmd); +} + +// #functionsCollection save functions in memory +client.functions = new Discord.Collection(); +const functionFiles = fs.readdirSync("./functions").filter(file => file.endsWith(".js")); +for (const file of functionFiles) { + const func = require(`./functions/${file}`); + client.functions.set(func.name, func); +} + +// #cooldownCollection save cooldowns in memory +const cooldowns = new Discord.Collection(); + +// #parameter +const param = { + activity, + client, + cmdName: "", + commandName : "", + config, + cooldowns, + db, + dbPrefix, + defConfig, + Discord, + isOwner : false, + isAdmin : false, + isModerator : false, + MessageAttachment, + moment, + process, +}; +// add every functions to param object +client.functions.forEach(fn => param[fn.name] = client.functions.get(fn.name)); + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~EVENT HANDLER~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +fs.readdir("./events/", (err, files) => { + if(err) return console.log(err); + files.forEach(file => { + const event = require(`./events/${file}`); + const eventName = event.name || file.split(".")[0]; + + if(event.disabled) return console.log(`> ${eventName} is disabled.`); + + try { + client[event.once ? "once" : "on"](eventName, async (...args) => await event.execute(param, ...args)); + } + catch (error) { + console.log(error.stack); + } + }); +}); + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~LOGIN~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +keepAlive(); +client.login(process.env.TOKEN); +require("https").createServer().listen(); + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/package.json b/package.json index 4e2fcd0..f2ad549 100644 --- a/package.json +++ b/package.json @@ -29,4 +29,4 @@ "discord-bot", "modmail-bot" ] -} +} \ No newline at end of file diff --git a/server.js b/server.js index cf1243d..37bd932 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,9 @@ -const express = require('express'); +const express = require("express"); const server = express(); -server.all('/', (req, res) => { - res.send('Bot is running.') -}) -function keepAlive(){ - server.listen(3000, () => {console.log("Server is ready.")}) +server.all("/", (req, res) => { + res.send("Bot is running."); +}); +function keepAlive() { + server.listen(3000, () => {console.log("Server is ready.");}); } module.exports = keepAlive; \ No newline at end of file