├── config.example.json ├── events ├── ready.js ├── messageCreate.js └── interactionCreate.js ├── SlashCommands ├── Info │ ├── ping.js │ ├── help.js │ └── userinfo.js ├── Mod │ ├── lock.js │ ├── unlock.js │ ├── unwarn.js │ ├── slowmode.js │ ├── unban.js │ ├── ban.js │ ├── kick.js │ ├── clear.js │ ├── warn.js │ ├── removerole.js │ ├── nickname.js │ ├── addrole.js │ ├── warnings.js │ └── timeout.js └── Config │ └── setmodlogs.js ├── models ├── modlogs.js └── warnModel.js ├── index.js ├── package.json ├── handler ├── index.js └── functions.js ├── README.md └── LICENSE /config.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "token": "", 3 | "mongooseConnectionString": "", 4 | "fail": "", 5 | "success": "", 6 | "ban": "" 7 | } -------------------------------------------------------------------------------- /events/ready.js: -------------------------------------------------------------------------------- 1 | const client = require("../index"); 2 | 3 | client.on("ready", () => { 4 | client.user.setActivity(`${client.guilds.cache.size} guilds`, { 5 | type: "COMPETING", 6 | }); 7 | console.log(`✔️ ${client.user.tag}`); 8 | }); 9 | -------------------------------------------------------------------------------- /SlashCommands/Info/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "ping", 3 | description: "websocket ping", 4 | category: "info", 5 | 6 | run: async (client, interaction) => { 7 | interaction.followUp({ content: `${client.ws.ping} ms` }); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /models/modlogs.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const modLogModel = mongoose.model( 4 | "modlogs", 5 | new mongoose.Schema({ 6 | Guild: { type: String }, 7 | Channel: { type: String }, 8 | Enabled: { type: Boolean, default: false }, 9 | }) 10 | ); 11 | 12 | module.exports = modLogModel; 13 | -------------------------------------------------------------------------------- /models/warnModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | module.exports = mongoose.model( 4 | "warnings", 5 | new mongoose.Schema({ 6 | userId: { type: String }, 7 | guildId: { type: String }, 8 | moderatorId: { type: String }, 9 | reason: { type: String }, 10 | timestamp: { type: Number }, 11 | }) 12 | ); 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { Client, Collection } = require("discord.js"); 2 | 3 | const client = new Client({ 4 | intents: 32767, 5 | }); 6 | module.exports = client; 7 | 8 | client.slashCommands = new Collection(); 9 | client.config = require("./config.json"); 10 | client.setMaxListeners(50); 11 | require("events").defaultMaxListeners = 50; 12 | 13 | require("./handler")(client); 14 | 15 | client.login(client.config.token); 16 | -------------------------------------------------------------------------------- /events/messageCreate.js: -------------------------------------------------------------------------------- 1 | const client = require("../index"); 2 | 3 | client.on("messageCreate", async (message) => { 4 | if ( 5 | message.content === `<@${client.user.id}>` || 6 | message.content === `<@!${client.user.id}>` 7 | ) 8 | return message.channel.send({ 9 | content: `Hi ${message.author} I'm **${client.user.username}**\nA powerful slash Moderation Discord bot`, 10 | }); 11 | 12 | if (message.author.bot || !message.guild) return; 13 | }); 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaf", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "nodemon index.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "Apache-2.0", 13 | "dependencies": { 14 | "discord.js": "^13.6.0", 15 | "glob": "^7.2.0", 16 | "leaf-utils": "^1.1.3", 17 | "mongoose": "^6.1.4", 18 | "ms": "^2.1.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /handler/index.js: -------------------------------------------------------------------------------- 1 | const { glob } = require("glob"); 2 | const { promisify } = require("util"); 3 | const { Client } = require("discord.js"); 4 | const mongoose = require("mongoose"); 5 | 6 | const globPromise = promisify(glob); 7 | 8 | module.exports = async (client) => { 9 | 10 | const eventFiles = await globPromise(`${process.cwd()}/events/*.js`); 11 | eventFiles.map((value) => require(value)); 12 | 13 | 14 | const slashCommands = await globPromise( 15 | `${process.cwd()}/SlashCommands/*/*.js` 16 | ); 17 | 18 | const arrayOfSlashCommands = []; 19 | slashCommands.map((value) => { 20 | const file = require(value); 21 | if (!file?.name) return; 22 | client.slashCommands.set(file.name, file); 23 | 24 | if (["MESSAGE", "USER"].includes(file.type)) delete file.description; 25 | arrayOfSlashCommands.push(file); 26 | }); 27 | client.on("ready", async () => { 28 | await client.application.commands.set(arrayOfSlashCommands); 29 | }); 30 | 31 | const { mongooseConnectionString } = require('../config.json') 32 | if (!mongooseConnectionString) return; 33 | 34 | mongoose.connect(mongooseConnectionString).then(() => console.log('✔️ MongoDB')); 35 | }; 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Moderation Discord Bot 3 |

4 |

5 | 6 | [![discordBadge](https://img.shields.io/badge/Chat-Coding%20Planet-7289d9?style=for-the-badge&logo=discord)](https://discord.gg/yfD2Vmnr6F) 7 |

8 | 9 | ## About 10 | A powerful Slash `Moderation` Discord Bot 11 | 12 | ## Installation 13 | To get started clone this repository and run `npm install`. 14 | 15 | ## Setting Up 16 | Rename `config.example.json` to `config.json` and fill the parameters 17 | ``` 18 | { 19 | "token": "your_bot_token", 20 | "mongooseConnectionString": "mongoURI", 21 | // emojis 22 | "fail": "", 23 | "success": "", 24 | "ban": "" 25 | } 26 | ``` 27 | 28 | Invite your Discord Bot with `bot` and `applications.commands` scope 29 | 30 | ## Note 31 | It can take up to 1 hour until the slash commands are displayed 32 | 33 | ## Projects 34 | - [Youtube](https://www.youtube.com/channel/UC9yRVadElzxSO3ZUywK6Yig) - My `Youtube Channel` 35 | - [Discord Server](https://discord.gg/yfD2Vmnr6F) - My `Discord Server` 36 | - [GitHub](https://github.com/notLeaf/leaf-utils) - Leaf-utils `Github` 37 | - [NPM](https://www.npmjs.com/package/leaf-utils) - Leaf-utils `NPM` 38 | 39 | ## Preview 40 | if you’re looking for a preview of the bot, check out my [youtube video](https://youtu.be/6t1Dp2iC_oc) 41 | -------------------------------------------------------------------------------- /events/interactionCreate.js: -------------------------------------------------------------------------------- 1 | const { fail } = require("../config.json"); 2 | const client = require("../index"); 3 | 4 | client.on("interactionCreate", async (interaction) => { 5 | if (interaction.isCommand()) { 6 | await interaction.deferReply({ ephemeral: false }).catch(() => {}); 7 | 8 | const cmd = client.slashCommands.get(interaction.commandName); 9 | if (!cmd) 10 | return interaction.followUp({ content: "An error has occured " }); 11 | 12 | const args = []; 13 | 14 | for (let option of interaction.options.data) { 15 | if (option.type === "SUB_COMMAND") { 16 | if (option.name) args.push(option.name); 17 | option.options?.forEach((x) => { 18 | if (x.value) args.push(x.value); 19 | }); 20 | } else if (option.value) args.push(option.value); 21 | } 22 | interaction.member = interaction.guild.members.cache.get( 23 | interaction.user.id 24 | ); 25 | 26 | if ( 27 | !interaction.guild.roles.everyone.permissions.has( 28 | "USE_EXTERNAL_EMOJIS" 29 | ) 30 | ) { 31 | return interaction.followUp({ 32 | content: 33 | "The `@everyone` role is missing the Permission to `USE_EXTERNAL_EMOJIS`, enable it", 34 | }); 35 | } 36 | 37 | cmd.run(client, interaction, args); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /SlashCommands/Mod/lock.js: -------------------------------------------------------------------------------- 1 | const { modLog } = require("../../handler/functions"); 2 | const { fail, success } = require("../../config.json"); 3 | 4 | module.exports = { 5 | name: "lock", 6 | description: "locks a channel", 7 | category: "mod", 8 | options: [ 9 | { 10 | name: "channel", 11 | description: "channel to lock", 12 | type: "CHANNEL", 13 | channelTypes: ["GUILD_TEXT"], 14 | required: true, 15 | }, 16 | { 17 | name: "reason", 18 | description: "reason for this lock", 19 | type: "STRING", 20 | required: false, 21 | }, 22 | ], 23 | 24 | run: async (client, interaction) => { 25 | const channel = interaction.options.getChannel("channel"); 26 | const reason = 27 | interaction.options.getString("reason") || "`No Reason Provided`"; 28 | 29 | if ( 30 | !channel 31 | .permissionsFor(interaction.guild.roles.everyone) 32 | .has("SEND_MESSAGES") 33 | ) 34 | return interaction.followUp({ 35 | content: `${fail} ${channel} is already locked`, 36 | }); 37 | 38 | channel.permissionOverwrites.edit(interaction.guild.id, { 39 | SEND_MESSAGES: false, 40 | }); 41 | 42 | interaction.editReply({ 43 | content: `${success} ${channel} locked successfully!`, 44 | }); 45 | 46 | modLog(interaction, reason, { 47 | Action: "`Lock`", 48 | Channel: `${channel}`, 49 | }); 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /SlashCommands/Mod/unlock.js: -------------------------------------------------------------------------------- 1 | const { modLog } = require("../../handler/functions"); 2 | const { fail, success } = require("../../config.json"); 3 | 4 | module.exports = { 5 | name: "unlock", 6 | description: "unlocks a channel", 7 | category: "mod", 8 | options: [ 9 | { 10 | name: "channel", 11 | description: "channel to lock", 12 | type: "CHANNEL", 13 | channelTypes: ["GUILD_TEXT"], 14 | required: true, 15 | }, 16 | { 17 | name: "reason", 18 | description: "reason for this unlock", 19 | type: "STRING", 20 | required: false, 21 | }, 22 | ], 23 | 24 | run: async (client, interaction) => { 25 | const channel = interaction.options.getChannel("channel"); 26 | const reason = 27 | interaction.options.getString("reason") || "`No Reason Provided`"; 28 | 29 | if ( 30 | channel 31 | .permissionsFor(interaction.guild.roles.everyone) 32 | .has("SEND_MESSAGES") 33 | ) 34 | return interaction.followUp({ 35 | content: `${fail} ${channel} is not locked`, 36 | }); 37 | 38 | channel.permissionOverwrites.edit(interaction.guild.id, { 39 | SEND_MESSAGES: true, 40 | }); 41 | 42 | interaction.editReply({ 43 | content: `${success} ${channel} unlocked successfully!`, 44 | }); 45 | 46 | modLog(interaction, reason, { 47 | Action: "`Unlock`", 48 | Channel: `${channel}`, 49 | }); 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /SlashCommands/Config/setmodlogs.js: -------------------------------------------------------------------------------- 1 | const { fail, success } = require("../../config.json"); 2 | const modLogModel = require("../../models/modlogs"); 3 | 4 | module.exports = { 5 | name: "modlogs", 6 | description: "sets the modlog channel", 7 | category: "config", 8 | options: [ 9 | { 10 | name: "enable", 11 | description: "enable the modlogs system", 12 | type: "SUB_COMMAND", 13 | options: [ 14 | { 15 | name: "channel", 16 | description: "modlogs channel", 17 | type: "CHANNEL", 18 | channelTypes: ["GUILD_TEXT"], 19 | required: true, 20 | }, 21 | ], 22 | }, 23 | { 24 | name: "disable", 25 | description: "disable the modlogs system", 26 | type: "SUB_COMMAND", 27 | }, 28 | ], 29 | 30 | run: async (client, interaction) => { 31 | const data = 32 | (await modLogModel.findOne({ 33 | Guild: interaction.guildId, 34 | })) || 35 | (await modLogModel.create({ 36 | Guild: interaction.guildId, 37 | })); 38 | channel = interaction.options.getChannel("channel"); 39 | command = interaction.options.getSubcommand(); 40 | 41 | if (command === "enable") { 42 | if (data.Channel === channel.id) 43 | return interaction.followUp({ 44 | content: `${fail} This is already the modlogs channel`, 45 | }); 46 | 47 | data.Channel = channel.id; 48 | data.Enabled = true; 49 | data.save(); 50 | 51 | interaction.followUp({ 52 | content: `${success} Logs have been set to ${channel}`, 53 | }); 54 | } else if (command === "disable") { 55 | if (data.Enabled !== true) 56 | return interaction.followUp({ 57 | content: `${fail} Modlogs system is already disabled`, 58 | }); 59 | 60 | data.Channel = null; 61 | data.Enabled = false; 62 | data.save(); 63 | 64 | interaction.followUp({ 65 | content: `${success} Modlogs system has been disabled`, 66 | }); 67 | } 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /SlashCommands/Mod/unwarn.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | const warnModel = require("../../models/warnModel"); 9 | 10 | module.exports = { 11 | name: "unwarn", 12 | description: "unwarns a member in your server", 13 | category: "mod", 14 | options: [ 15 | { 16 | name: "warnid", 17 | description: "warnId you want to delete", 18 | type: "STRING", 19 | required: true, 20 | }, 21 | { 22 | name: "reason", 23 | description: "reason for this warn", 24 | type: "STRING", 25 | required: false, 26 | }, 27 | ], 28 | 29 | run: async (client, interaction) => { 30 | try { 31 | const warnId = interaction.options.getString("warnid"); 32 | data = await warnModel.findById(warnId); 33 | user = interaction.guild.members.cache.get(data.userId); 34 | reason = 35 | interaction.options.getString("reason") || 36 | "`No Reason Provided`"; 37 | 38 | const embed = new MessageEmbed() 39 | .setAuthor({ 40 | name: `${interaction.user.tag}`, 41 | iconURL: interaction.user.displayAvatarURL({ 42 | dynamic: true, 43 | }), 44 | }) 45 | .setDescription( 46 | `**${interaction.user.tag}** are you sure you want to unwarn ${user}` 47 | ) 48 | .setFooter({ 49 | text: client.user.tag, 50 | iconURL: client.user.displayAvatarURL(), 51 | }) 52 | .setColor(randomHex()) 53 | .setTimestamp(); 54 | 55 | confirmButtons(interaction, { 56 | embed: embed, 57 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 58 | yes: { 59 | style: "PRIMARY", 60 | label: "Unwarn", 61 | emoji: "✔️", 62 | }, 63 | no: { 64 | style: "SECONDARY", 65 | label: "Cancel", 66 | emoji: "🛑", 67 | }, 68 | }).then(async (confirm) => { 69 | if (confirm === "yes") { 70 | await data.delete(); 71 | interaction.editReply({ 72 | content: `${success} Unwarned **${user}** successfully!`, 73 | }); 74 | modLog(interaction, reason, { 75 | Action: "`Unwarn`", 76 | Member: `${user}`, 77 | }); 78 | } 79 | if (confirm === "no") { 80 | interaction.editReply({ 81 | content: `${fail} cancelled!`, 82 | }); 83 | } 84 | if (confirm === "time") { 85 | interaction.editReply({ 86 | content: `${fail} Time is up`, 87 | }); 88 | } 89 | }); 90 | } catch (e) { 91 | return interaction.followUp({ 92 | content: `${fail} This is not a valid warnID`, 93 | }); 94 | } 95 | }, 96 | }; 97 | -------------------------------------------------------------------------------- /SlashCommands/Mod/slowmode.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { modLog, randomHex } = require("../../handler/functions"); 3 | const { fail } = require("../../config.json"); 4 | const ms = require("ms"); 5 | 6 | module.exports = { 7 | name: "slowmode", 8 | description: "slowmode command", 9 | category: "mod", 10 | options: [ 11 | { 12 | name: "rate", 13 | description: "rate limit between 1 and 21600", 14 | type: "INTEGER", 15 | required: true, 16 | choices: [ 17 | { name: "off", value: 0 }, 18 | { name: "5 seconds", value: 5 }, 19 | { name: "10 seconds", value: 10 }, 20 | { name: "15 seconds", value: 15 }, 21 | { name: "30 seconds", value: 30 }, 22 | { name: "1 minute", value: 60 }, 23 | { name: "2 minutes", value: 120 }, 24 | { name: "5 minutes", value: 300 }, 25 | { name: "10 minutes", value: 600 }, 26 | { name: "15 minutes", value: 900 }, 27 | { name: "30 minutes", value: 1800 }, 28 | { name: "1 hour", value: 3600 }, 29 | { name: "2 hours", value: 7200 }, 30 | { name: "6 hours", value: 21600 }, 31 | ], 32 | }, 33 | { 34 | name: "channel", 35 | description: 36 | "enables slowmode in a channel with the specified rate", 37 | type: "CHANNEL", 38 | channelTypes: ["GUILD_TEXT"], 39 | required: false, 40 | }, 41 | { 42 | name: "reason", 43 | description: "reason for this slowmode", 44 | type: "STRING", 45 | required: false, 46 | }, 47 | ], 48 | 49 | run: async (client, interaction) => { 50 | const rate = interaction.options.getInteger("rate"); 51 | channel = 52 | interaction.options.getChannel("channel") || interaction.channel; 53 | reason = 54 | interaction.options.getString("reason") || "`No Reason Provided`"; 55 | 56 | if (rate === 0 && !channel.rateLimitPerUser) 57 | return interaction.followUp({ 58 | content: `${fail} Slowmode is already off`, 59 | }); 60 | 61 | await channel.setRateLimitPerUser(rate, reason); 62 | 63 | const embed = new MessageEmbed() 64 | .setTitle("Slowmode") 65 | .setFooter({ 66 | text: client.user.tag, 67 | iconURL: client.user.displayAvatarURL(), 68 | }) 69 | .setTimestamp() 70 | .setColor(randomHex()); 71 | 72 | interaction.followUp({ 73 | embeds: [ 74 | embed 75 | .addField("Moderator", `\`${interaction.user.tag}\``, true) 76 | .addField("Channel", `${channel}`, true) 77 | .addField( 78 | "Rate", 79 | rate > 0 80 | ? `\`${ms(rate * 1e3, { long: true })}\`` 81 | : "`off`", 82 | true 83 | ) 84 | .addField("Reason", `${reason}`), 85 | ], 86 | }); 87 | 88 | modLog(interaction, reason, { 89 | Action: "`Slowmode`", 90 | Channel: `${channel}`, 91 | Rate: rate > 0 ? `\`${ms(rate * 1e3, { long: true })}\`` : "`off`", 92 | }); 93 | }, 94 | }; 95 | -------------------------------------------------------------------------------- /SlashCommands/Info/help.js: -------------------------------------------------------------------------------- 1 | const { SelectMenus } = require("leaf-utils"); 2 | 3 | module.exports = { 4 | name: "help", 5 | description: "Displays a list of all current commands", 6 | category: "info", 7 | 8 | run: async (client, interaction) => { 9 | const pages = [ 10 | { 11 | title: client.user.username + "'s commands", 12 | description: "Click this menu for more info", 13 | footer: { 14 | text: client.user.tag, 15 | icon_url: client.user.displayAvatarURL(), 16 | }, 17 | color: interaction.guild.me.displayHexColor, 18 | timestamp: new Date(), 19 | }, 20 | { 21 | title: "Config command", 22 | description: client.slashCommands 23 | .filter((cmd) => cmd.category === "config") 24 | .map((cmd) => `\`${cmd.name}\``) 25 | .join(", "), 26 | footer: { 27 | text: client.user.tag, 28 | icon_url: client.user.displayAvatarURL(), 29 | }, 30 | color: "#F3AA05", 31 | timestamp: new Date(), 32 | }, 33 | { 34 | title: "Info commands", 35 | description: client.slashCommands 36 | .filter((cmd) => cmd.category === "info") 37 | .map((cmd) => `\`${cmd.name}\``) 38 | .join(", "), 39 | footer: { 40 | text: client.user.tag, 41 | icon_url: client.user.displayAvatarURL(), 42 | }, 43 | color: "#F3AA05", 44 | timestamp: new Date(), 45 | }, 46 | { 47 | title: "Mod commands", 48 | description: client.slashCommands 49 | .filter((cmd) => cmd.category === "mod") 50 | .map((cmd) => `\`${cmd.name}\``) 51 | .join(", "), 52 | footer: { 53 | text: client.user.tag, 54 | icon_url: client.user.displayAvatarURL(), 55 | }, 56 | color: "#F3AA05", 57 | timestamp: new Date(), 58 | }, 59 | ], 60 | options = [ 61 | { 62 | label: "Home", 63 | emoji: "🏠", 64 | }, 65 | { 66 | label: "Configuration", 67 | emoji: "⚙️", 68 | }, 69 | { 70 | label: "Info", 71 | emoji: "ℹ️", 72 | }, 73 | { 74 | label: "Moderation", 75 | emoji: "🔨", 76 | }, 77 | ]; 78 | 79 | await SelectMenus({ 80 | message: interaction, 81 | slash_command: true, 82 | time: 300000, 83 | pages: pages, 84 | options: options, 85 | authorOnly: { 86 | enabled: true, 87 | ephemeral: true, 88 | authorMessage: "Only <@{{author}}> can use this menu", 89 | }, 90 | placeholder: "Help Menu", 91 | }); 92 | }, 93 | }; 94 | -------------------------------------------------------------------------------- /SlashCommands/Mod/unban.js: -------------------------------------------------------------------------------- 1 | const { Client, CommandInteraction, MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | 9 | module.exports = { 10 | name: "unban", 11 | description: "unbans a member from your server", 12 | category: "mod", 13 | options: [ 14 | { 15 | name: "userid", 16 | description: "userid you want to unban", 17 | type: "STRING", 18 | required: true, 19 | }, 20 | { 21 | name: "reason", 22 | description: "reason for this unban", 23 | type: "STRING", 24 | required: false, 25 | }, 26 | ], 27 | 28 | run: async (client, interaction) => { 29 | try { 30 | const id = interaction.options.getString("userid"); 31 | reason = 32 | interaction.options.getString("reason") || 33 | "`No Reason Provided`"; 34 | user = await client.users.fetch(id); 35 | bans = await interaction.guild.bans.fetch(); 36 | bannedusers = bans.find((b) => b.user.id == id); 37 | 38 | if (!bannedusers) 39 | return interaction.followUp({ 40 | content: `${fail} \`${user.tag}\` is not banned`, 41 | }); 42 | 43 | const embed = new MessageEmbed() 44 | .setAuthor({ 45 | name: `${interaction.user.tag}`, 46 | iconURL: interaction.user.displayAvatarURL({ 47 | dynamic: true, 48 | }), 49 | }) 50 | .setDescription( 51 | `**${interaction.user.tag}** are you sure you want to unban **${user.tag}**` 52 | ) 53 | .setFooter({ 54 | text: client.user.tag, 55 | iconURL: client.user.displayAvatarURL(), 56 | }) 57 | .setColor(randomHex()) 58 | .setTimestamp(); 59 | 60 | confirmButtons(interaction, { 61 | embed: embed, 62 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 63 | yes: { 64 | style: "SUCCESS", 65 | label: "Unban", 66 | emoji: "✔️", 67 | }, 68 | no: { 69 | style: "SECONDARY", 70 | label: "Cancel", 71 | emoji: "🛑", 72 | }, 73 | }).then(async (confirm) => { 74 | if (confirm === "yes") { 75 | await interaction.guild.members.unban(user, reason); 76 | interaction.editReply({ 77 | content: `${success} Unbanned **${user.tag}** successfully!`, 78 | }); 79 | modLog(interaction, reason, { 80 | Action: "`Unban`", 81 | Member: `\`${user.tag}\``, 82 | }); 83 | } 84 | if (confirm === "no") { 85 | interaction.editReply({ 86 | content: `${fail} cancelled!`, 87 | }); 88 | } 89 | if (confirm === "time") { 90 | interaction.editReply({ 91 | content: `${fail} Time is up`, 92 | }); 93 | } 94 | }); 95 | } catch (e) { 96 | return interaction.followUp({ 97 | content: `${fail} This is not a valid user`, 98 | }); 99 | } 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /SlashCommands/Info/userinfo.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const statuses = { 3 | online: `\`Online\``, 4 | idle: `\`AFK\``, 5 | undefined: `\`Offline\``, // offline members don't have a presence 6 | dnd: `\`Do Not Disturb\``, 7 | }; 8 | const flags = { 9 | DISCORD_EMPLOYEE: `\`Discord Employee\``, 10 | DISCORD_PARTNER: `\`Partnered Server Owner\``, 11 | BUGHUNTER_LEVEL_1: `\`Bug Hunter (Level 1)\``, 12 | BUGHUNTER_LEVEL_2: `\`Bug Hunter (Level 2)\``, 13 | HYPESQUAD_EVENTS: `\`HypeSquad Events\``, 14 | HOUSE_BRAVERY: `\`House of Bravery\``, 15 | HOUSE_BRILLIANCE: `\`House of Brilliance\``, 16 | HOUSE_BALANCE: `\`House of Balance\``, 17 | EARLY_SUPPORTER: `\`Early Supporter\``, 18 | TEAM_USER: "Team User", 19 | SYSTEM: "System", 20 | VERIFIED_BOT: `\`Verified Bot\``, 21 | VERIFIED_DEVELOPER: `\`Early Verified Bot Developer\``, 22 | }; 23 | 24 | module.exports = { 25 | name: "userinfo", 26 | description: "Displays the userinfo of the specified target.", 27 | category: "info", 28 | options: [ 29 | { 30 | name: "target", 31 | description: "Select the target.", 32 | type: "USER", 33 | required: false, 34 | }, 35 | ], 36 | 37 | run: async (client, interaction) => { 38 | const member = 39 | interaction.options.getMember("target") || interaction.member; 40 | const userFlags = (await member.user.fetchFlags()).toArray(); 41 | 42 | const embed = new MessageEmbed() 43 | .setAuthor({ 44 | name: `${member.user.tag}`, 45 | iconURL: member.user.displayAvatarURL({ 46 | dynamic: true, 47 | }), 48 | }) 49 | .addField( 50 | "Joined server", 51 | ``, 52 | true 53 | ) 54 | .addField( 55 | "Joined Discord", 56 | ``, 57 | true 58 | ) 59 | .addField("Highest Role", `${member.roles.highest}`, true) 60 | .addField("Status", `${statuses[member.presence?.status]}`, true) 61 | .addField("Bot", `\`${member.user.bot}\``, true) 62 | .setThumbnail(member.user.displayAvatarURL({ dynamic: true })) 63 | .setFooter({ 64 | text: "ID: " + member.id, 65 | }) 66 | .setColor("RED") 67 | .setTimestamp(); 68 | 69 | if (member.presence?.activities) { 70 | const activitytype = { 71 | PLAYING: "Playing", 72 | STREAMING: "Streaming", 73 | LISTENING: "Listening", 74 | WATCHING: "Watching", 75 | CUSTOM: "Custom Status", 76 | COMPETING: "Competing", 77 | }; 78 | 79 | let activitystring = ``; 80 | member.presence.activities.forEach((activity) => { 81 | activitystring += `\n**${activitytype[activity.type]}**${ 82 | activity.name == `Custom Status` 83 | ? `\n${activity.state}` 84 | : `\n${activity.name} ${ 85 | activity.details 86 | ? `- \`${activity.details}\`` 87 | : `` 88 | }` 89 | }`; 90 | }); 91 | if (activitystring.length > 0) embed.setDescription(activitystring); 92 | } 93 | 94 | if (userFlags.length > 0) 95 | embed.addField( 96 | "Badges", 97 | userFlags.map((flag) => flags[flag]).join("\n") 98 | ); 99 | 100 | interaction.followUp({ 101 | embeds: [embed], 102 | }); 103 | }, 104 | }; 105 | -------------------------------------------------------------------------------- /SlashCommands/Mod/ban.js: -------------------------------------------------------------------------------- 1 | const { Client, CommandInteraction, MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, ban } = require("../../config.json"); 8 | 9 | module.exports = { 10 | name: "ban", 11 | description: "ban a member", 12 | category: "mod", 13 | options: [ 14 | { 15 | name: "target", 16 | description: "target to ban", 17 | type: "USER", 18 | required: true, 19 | }, 20 | { 21 | name: "reason", 22 | description: "reason for this ban", 23 | type: "STRING", 24 | required: false, 25 | }, 26 | ], 27 | 28 | run: async (client, interaction) => { 29 | const target = interaction.options.getMember("target"); 30 | const reason = 31 | interaction.options.getString("reason") || "`No Reason Provided`"; 32 | 33 | if (target.id === interaction.member.id) 34 | return interaction.followUp({ 35 | content: `${fail} You cant ban yourself`, 36 | }); 37 | 38 | if (target.id === interaction.guild.me.id) 39 | return interaction.followUp({ 40 | content: `${fail} You cant ban me`, 41 | }); 42 | 43 | if (target.id === interaction.guild.ownerId) 44 | return interaction.followUp({ 45 | content: `${fail} You cannot ban the server owner`, 46 | }); 47 | 48 | if ( 49 | target.roles.highest.position >= 50 | interaction.member.roles.highest.position 51 | ) 52 | return interaction.followUp({ 53 | content: `${fail} This user is higher/equal than you`, 54 | }); 55 | 56 | if ( 57 | target.roles.highest.position >= 58 | interaction.guild.me.roles.highest.position 59 | ) 60 | return interaction.followUp({ 61 | content: `${fail} This user is higher/equal than me`, 62 | }); 63 | 64 | const embed = new MessageEmbed() 65 | .setAuthor({ 66 | name: `${interaction.user.tag}`, 67 | iconURL: interaction.user.displayAvatarURL({ 68 | dynamic: true, 69 | }), 70 | }) 71 | .setDescription( 72 | `**${interaction.user.tag}** are you sure you want to ban **${target.user.tag}**` 73 | ) 74 | .setFooter({ 75 | text: client.user.tag, 76 | iconURL: client.user.displayAvatarURL(), 77 | }) 78 | .setColor(randomHex()) 79 | .setTimestamp(); 80 | 81 | confirmButtons(interaction, { 82 | embed: embed, 83 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 84 | yes: { 85 | style: "PRIMARY", 86 | label: "Ban", 87 | emoji: `${ban}`, 88 | }, 89 | no: { 90 | style: "SECONDARY", 91 | label: "No", 92 | emoji: "🛑", 93 | }, 94 | }).then(async (confirm) => { 95 | if (confirm === "yes") { 96 | await target.ban({ 97 | reason, 98 | }); 99 | interaction.editReply({ 100 | content: `${ban} Banned **${target.user.tag}** successfully!`, 101 | }); 102 | modLog(interaction, reason, { 103 | Action: "`Ban`", 104 | Member: `\`${target.user.tag}\``, 105 | }); 106 | } 107 | if (confirm === "no") { 108 | interaction.editReply({ 109 | content: `${fail} **${target.user.tag}** hasn't been banned!`, 110 | }); 111 | } 112 | if (confirm === "time") { 113 | interaction.editReply({ 114 | content: `${fail} Time is up`, 115 | }); 116 | } 117 | }); 118 | }, 119 | }; 120 | -------------------------------------------------------------------------------- /SlashCommands/Mod/kick.js: -------------------------------------------------------------------------------- 1 | const { Client, CommandInteraction, MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | 9 | module.exports = { 10 | name: "kick", 11 | description: "kick a member", 12 | category: "mod", 13 | options: [ 14 | { 15 | name: "target", 16 | description: "target to kick", 17 | type: "USER", 18 | required: true, 19 | }, 20 | { 21 | name: "reason", 22 | description: "reason for this kick", 23 | type: "STRING", 24 | required: false, 25 | }, 26 | ], 27 | 28 | run: async (client, interaction) => { 29 | const target = interaction.options.getMember("target"); 30 | const reason = 31 | interaction.options.getString("reason") || "`No Reason Provided`"; 32 | 33 | if (target.id === interaction.member.id) 34 | return interaction.followUp({ 35 | content: `${fail} You cant kick yourself`, 36 | }); 37 | 38 | if (target.id === interaction.guild.me.id) 39 | return interaction.followUp({ 40 | content: `${fail} You cant kick me`, 41 | }); 42 | 43 | if (target.id === interaction.guild.ownerId) 44 | return interaction.followUp({ 45 | content: `${fail} You cannot kick the server owner`, 46 | }); 47 | 48 | if ( 49 | target.roles.highest.position >= 50 | interaction.member.roles.highest.position 51 | ) 52 | return interaction.followUp({ 53 | content: `${fail} This user is higher/equal than you`, 54 | }); 55 | 56 | if ( 57 | target.roles.highest.position >= 58 | interaction.guild.me.roles.highest.position 59 | ) 60 | return interaction.followUp({ 61 | content: `${fail} This user is higher/equal than me`, 62 | }); 63 | 64 | const embed = new MessageEmbed() 65 | .setAuthor({ 66 | name: `${interaction.user.tag}`, 67 | iconURL: interaction.user.displayAvatarURL({ 68 | dynamic: true, 69 | }), 70 | }) 71 | .setDescription( 72 | `**${interaction.user.tag}** are you sure you want to kick **${target.user.tag}**` 73 | ) 74 | .setFooter({ 75 | text: client.user.tag, 76 | iconURL: client.user.displayAvatarURL(), 77 | }) 78 | .setColor(randomHex()) 79 | .setTimestamp(); 80 | 81 | confirmButtons(interaction, { 82 | embed: embed, 83 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 84 | yes: { 85 | style: "SUCCESS", 86 | label: "Kick", 87 | emoji: "✔️", 88 | }, 89 | no: { 90 | style: "SECONDARY", 91 | label: "No", 92 | emoji: "🛑", 93 | }, 94 | }).then(async (confirm) => { 95 | if (confirm === "yes") { 96 | await target.kick(reason); 97 | interaction.editReply({ 98 | content: `${success} Kicked **${target.user.tag}** successfully!`, 99 | }); 100 | modLog(interaction, reason, { 101 | Action: "`Kick`", 102 | Member: `\`${target.user.tag}\``, 103 | }); 104 | } 105 | if (confirm === "no") { 106 | interaction.editReply({ 107 | content: `${fail} **${target.user.tag}** hasn't been kicked!`, 108 | }); 109 | } 110 | if (confirm === "time") { 111 | interaction.editReply({ 112 | content: `${fail} Time is up`, 113 | }); 114 | } 115 | }); 116 | }, 117 | }; 118 | -------------------------------------------------------------------------------- /SlashCommands/Mod/clear.js: -------------------------------------------------------------------------------- 1 | const { Client, CommandInteraction, MessageEmbed } = require("discord.js"); 2 | const { modLog, randomHex } = require("../../handler/functions"); 3 | const { fail } = require("../../config.json"); 4 | 5 | module.exports = { 6 | name: "clear", 7 | description: "deletes messages", 8 | category: "mod", 9 | options: [ 10 | { 11 | name: "amount", 12 | description: "Select the amount messages to delete", 13 | type: "INTEGER", 14 | required: true, 15 | }, 16 | { 17 | name: "channel", 18 | description: "channel to delete messages", 19 | type: "CHANNEL", 20 | required: false, 21 | }, 22 | { 23 | name: "target", 24 | description: "Select a target to clear their messages", 25 | type: "USER", 26 | required: false, 27 | }, 28 | { 29 | name: "reason", 30 | description: "reason for this purge", 31 | type: "STRING", 32 | required: false, 33 | }, 34 | ], 35 | 36 | run: async (client, interaction) => { 37 | const channel = 38 | interaction.options.getChannel("channel") || interaction.channel; 39 | member = interaction.options.getMember("target"); 40 | amount = interaction.options.getInteger("amount"); 41 | reason = 42 | interaction.options.getString("reason") || "`No Reason Provided`"; 43 | 44 | if (amount < 0 || amount > 100) 45 | return interaction.followUp({ 46 | content: `${fail} You can only delete 100 messages at once`, 47 | }); 48 | 49 | if (!channel.isText()) 50 | return interaction.followUp({ 51 | content: `${fail} Please select a text channel`, 52 | }); 53 | 54 | let messages; 55 | if (member) { 56 | messages = ( 57 | await channel.messages.fetch({ 58 | limit: amount, 59 | }) 60 | ).filter((m) => m.member.id === member.id); 61 | } else messages = amount; 62 | 63 | if (messages.size === 0) { 64 | return interaction.followup({ 65 | content: `${fail} Unable to find any messages from ${member}`, 66 | }); 67 | } else { 68 | await channel.bulkDelete(messages, true).then((messages) => { 69 | const embed = new MessageEmbed() 70 | .setDescription( 71 | ` 72 | Successfully deleted **${messages.size}** message(s). 73 | ` 74 | ) 75 | .addField("Channel", `${channel}`, true) 76 | .addField("Message Count", `\`${messages.size}\``, true) 77 | .addField("Reason", `${reason}`, true) 78 | .setTimestamp() 79 | .setColor(randomHex()); 80 | 81 | if (member) { 82 | embed 83 | .spliceFields(1, 1, { 84 | name: "Found Messages", 85 | value: `\`${messages.size}\``, 86 | inline: true, 87 | }) 88 | .spliceFields(1, 0, { 89 | name: "Member", 90 | value: `${member}`, 91 | inline: true, 92 | }); 93 | } 94 | interaction 95 | .editReply({ 96 | embeds: [embed], 97 | }) 98 | .catch(() => {}); 99 | }); 100 | } 101 | 102 | const fields = { 103 | Action: "`Clear`", 104 | Channel: `${channel}`, 105 | }; 106 | 107 | if (member) { 108 | fields["Member"] = `${member}`; 109 | fields["Found Messages"] = `\`${messages.size}\``; 110 | } else fields["Message Count"] = `\`${amount}\``; 111 | 112 | modLog(interaction, reason, fields); 113 | }, 114 | }; 115 | -------------------------------------------------------------------------------- /SlashCommands/Mod/warn.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | const warnModel = require("../../models/warnModel"); 9 | 10 | module.exports = { 11 | name: "warn", 12 | description: "warns a member in your server", 13 | category: "mod", 14 | options: [ 15 | { 16 | name: "target", 17 | description: "target to warn", 18 | type: "USER", 19 | required: true, 20 | }, 21 | { 22 | name: "reason", 23 | description: "reason for this warn", 24 | type: "STRING", 25 | required: false, 26 | }, 27 | ], 28 | 29 | run: async (client, interaction) => { 30 | const target = interaction.options.getMember("target"); 31 | const reason = 32 | interaction.options.getString("reason") || "`No Reason Provided`"; 33 | 34 | if (target.id === interaction.member.id) 35 | return interaction.followUp({ 36 | content: `${fail} You cant warn yourself`, 37 | }); 38 | 39 | if (target.id === interaction.guild.me.id) 40 | return interaction.followUp({ 41 | content: `${fail} You cant warn me`, 42 | }); 43 | 44 | if (target.id === interaction.guild.ownerId) 45 | return interaction.followUp({ 46 | content: `${fail} You cannot warn the server owner`, 47 | }); 48 | 49 | if ( 50 | target.roles.highest.position >= 51 | interaction.member.roles.highest.position 52 | ) 53 | return interaction.followUp({ 54 | content: `${fail} This user is higher/equal than you`, 55 | }); 56 | 57 | if ( 58 | target.roles.highest.position >= 59 | interaction.guild.me.roles.highest.position 60 | ) 61 | return interaction.followUp({ 62 | content: `${fail} This user is higher/equal than me`, 63 | }); 64 | 65 | const embed = new MessageEmbed() 66 | .setAuthor({ 67 | name: `${interaction.user.tag}`, 68 | iconURL: interaction.user.displayAvatarURL({ 69 | dynamic: true, 70 | }), 71 | }) 72 | .setDescription( 73 | `**${interaction.user.tag}** are you sure you want to warn **${target.user.tag}**` 74 | ) 75 | .setFooter({ 76 | text: client.user.tag, 77 | iconURL: client.user.displayAvatarURL(), 78 | }) 79 | .setColor(randomHex()) 80 | .setTimestamp(); 81 | 82 | confirmButtons(interaction, { 83 | embed: embed, 84 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 85 | yes: { 86 | style: "PRIMARY", 87 | label: "Warn", 88 | emoji: "✔️", 89 | }, 90 | no: { 91 | style: "SECONDARY", 92 | label: "No", 93 | emoji: "🛑", 94 | }, 95 | }).then(async (confirm) => { 96 | if (confirm === "yes") { 97 | await new warnModel({ 98 | userId: target.id, 99 | guildId: interaction.guildId, 100 | moderatorId: interaction.user.id, 101 | reason, 102 | timestamp: Date.now(), 103 | }).save(); 104 | interaction.editReply({ 105 | content: `${success} Warned **${target.user.tag}** successfully!`, 106 | }); 107 | modLog(interaction, reason, { 108 | Action: "`Warn`", 109 | Member: `${target}`, 110 | }); 111 | } 112 | if (confirm === "no") { 113 | interaction.editReply({ 114 | content: `${fail} **${target.user.tag}** hasn't been warned!`, 115 | }); 116 | } 117 | if (confirm === "time") { 118 | interaction.editReply({ 119 | content: `${fail} Time is up`, 120 | }); 121 | } 122 | }); 123 | }, 124 | }; 125 | -------------------------------------------------------------------------------- /SlashCommands/Mod/removerole.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | 9 | module.exports = { 10 | name: "removerole", 11 | description: "removes the specified role from the provided user", 12 | category: "mod", 13 | options: [ 14 | { 15 | name: "target", 16 | description: "target to remove role to", 17 | type: "USER", 18 | required: true, 19 | }, 20 | { 21 | name: "role", 22 | description: "role to remove", 23 | type: "ROLE", 24 | required: true, 25 | }, 26 | { 27 | name: "reason", 28 | description: "reason", 29 | type: "STRING", 30 | required: false, 31 | }, 32 | ], 33 | 34 | run: async (client, interaction) => { 35 | const target = interaction.options.getMember("target"); 36 | role = interaction.options.getRole("role"); 37 | reason = 38 | interaction.options.getString("reason") || "`No Reason Provided`"; 39 | 40 | if ( 41 | target.roles.highest.position >= 42 | interaction.member.roles.highest.position 43 | ) 44 | return interaction.followUp({ 45 | content: `${fail} This user is higher/equal than you`, 46 | }); 47 | 48 | if ( 49 | target.roles.highest.position >= 50 | interaction.guild.me.roles.highest.position 51 | ) 52 | return interaction.followUp({ 53 | content: `${fail} This user is higher/equal than me`, 54 | }); 55 | 56 | if (role.position >= interaction.member.roles.highest.position) 57 | return interaction.followUp({ 58 | content: `${fail} That role is higher/equal than you`, 59 | }); 60 | 61 | if (role.position >= interaction.guild.me.roles.highest.position) 62 | return interaction.followUp({ 63 | content: `${fail} That role is higher/equal than me`, 64 | }); 65 | 66 | if (!target.roles.cache.has(role.id)) 67 | return interaction.followUp({ 68 | content: `${fail} User does not have the provided role`, 69 | }); 70 | 71 | const embed = new MessageEmbed() 72 | .setAuthor({ 73 | name: `${interaction.user.tag}`, 74 | iconURL: interaction.user.displayAvatarURL({ 75 | dynamic: true, 76 | }), 77 | }) 78 | .setDescription( 79 | `**${interaction.user.tag}** are you sure you want to remove from **${target.user.tag}** the ${role} role` 80 | ) 81 | .setFooter({ 82 | text: client.user.tag, 83 | iconURL: client.user.displayAvatarURL(), 84 | }) 85 | .setColor(randomHex()) 86 | .setTimestamp(); 87 | 88 | confirmButtons(interaction, { 89 | embed: embed, 90 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 91 | yes: { 92 | style: "SUCCESS", 93 | label: "Remove", 94 | emoji: "✔️", 95 | }, 96 | no: { 97 | style: "SECONDARY", 98 | label: "Cancel", 99 | emoji: "🛑", 100 | }, 101 | }).then(async (confirm) => { 102 | if (confirm === "yes") { 103 | await target.roles.remove(role); 104 | interaction.editReply({ 105 | content: `${success} **${role.name}** was successfully removed from **${target.user.tag}**`, 106 | }); 107 | modLog(interaction, reason, { 108 | Action: "`Remove Role`", 109 | Member: `${target}`, 110 | Role: `${role}`, 111 | }); 112 | } 113 | if (confirm === "no") { 114 | interaction.editReply({ 115 | content: `${fail} **${target.user.tag}** cancelled!`, 116 | }); 117 | } 118 | if (confirm === "time") { 119 | interaction.editReply({ 120 | content: `${fail} Time is up`, 121 | }); 122 | } 123 | }); 124 | }, 125 | }; 126 | -------------------------------------------------------------------------------- /SlashCommands/Mod/nickname.js: -------------------------------------------------------------------------------- 1 | const { Client, CommandInteraction, MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | 9 | module.exports = { 10 | name: "nickname", 11 | description: "changes the provided user's nickname to the one specified", 12 | category: "mod", 13 | options: [ 14 | { 15 | name: "target", 16 | description: "target you want to change the nickname", 17 | type: "USER", 18 | required: true, 19 | }, 20 | { 21 | name: "nick", 22 | description: "new nickname", 23 | type: "STRING", 24 | required: true, 25 | }, 26 | { 27 | name: "reason", 28 | description: "reason for this action", 29 | type: "STRING", 30 | required: false, 31 | }, 32 | ], 33 | 34 | run: async (client, interaction) => { 35 | const target = interaction.options.getMember("target"); 36 | newnick = interaction.options.getString("nick"); 37 | reason = 38 | interaction.options.getString("reason") || "`No Reason Provided`"; 39 | 40 | if (target.id === interaction.guild.me.id) 41 | return interaction.followUp({ 42 | content: `${fail} You cant change my name`, 43 | }); 44 | 45 | if (target.id === interaction.guild.ownerId) 46 | return interaction.followUp({ 47 | content: `${fail} You cannot modify the server owner`, 48 | }); 49 | 50 | if ( 51 | target.roles.highest.position >= 52 | interaction.member.roles.highest.position 53 | ) 54 | return interaction.followUp({ 55 | content: `${fail} This user is higher/equal than you`, 56 | }); 57 | 58 | if ( 59 | target.roles.highest.position >= 60 | interaction.guild.me.roles.highest.position 61 | ) 62 | return interaction.followUp({ 63 | content: `${fail} This user is higher/equal than me`, 64 | }); 65 | 66 | if (newnick.length > 32) 67 | return interaction.followUp({ 68 | content: `${fail} Please ensure the nickname is no larger than 32 characters`, 69 | }); 70 | 71 | const embed = new MessageEmbed() 72 | .setAuthor({ 73 | name: `${interaction.user.tag}`, 74 | iconURL: interaction.user.displayAvatarURL({ 75 | dynamic: true, 76 | }), 77 | }) 78 | .setDescription( 79 | `**${interaction.user.tag}** are you sure you want to change **${target.user.username}**'s nickname` 80 | ) 81 | .setFooter({ 82 | text: client.user.tag, 83 | iconURL: client.user.displayAvatarURL(), 84 | }) 85 | .setColor(randomHex()) 86 | .setTimestamp(); 87 | 88 | confirmButtons(interaction, { 89 | embed: embed, 90 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 91 | yes: { 92 | style: "PRIMARY", 93 | label: "Yes", 94 | emoji: "✔️", 95 | }, 96 | no: { 97 | style: "SECONDARY", 98 | label: "No", 99 | emoji: "🛑", 100 | }, 101 | }).then(async (confirm) => { 102 | if (confirm === "yes") { 103 | await target.setNickname(newnick); 104 | interaction.editReply({ 105 | content: `${success} **${target.user.username}**'s nickname was successfully updated. **(${newnick})**`, 106 | }); 107 | modLog(interaction, reason, { 108 | Action: "`Nickname`", 109 | Member: `${target}`, 110 | Nickname: `\`${newnick}\``, 111 | }); 112 | } 113 | if (confirm === "no") { 114 | interaction.editReply({ 115 | content: `${fail} cancelled!`, 116 | }); 117 | } 118 | if (confirm === "time") { 119 | interaction.editReply({ 120 | content: `${fail} Time is up`, 121 | }); 122 | } 123 | }); 124 | }, 125 | }; 126 | -------------------------------------------------------------------------------- /SlashCommands/Mod/addrole.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | 9 | module.exports = { 10 | name: "addrole", 11 | description: "adds the specified role to the provided user", 12 | category: "mod", 13 | options: [ 14 | { 15 | name: "target", 16 | description: "target to add role to", 17 | type: "USER", 18 | required: true, 19 | }, 20 | { 21 | name: "role", 22 | description: "role to add", 23 | type: "ROLE", 24 | required: true, 25 | }, 26 | { 27 | name: "reason", 28 | description: "reason", 29 | type: "STRING", 30 | required: false, 31 | }, 32 | ], 33 | 34 | run: async (client, interaction) => { 35 | const target = interaction.options.getMember("target"); 36 | role = interaction.options.getRole("role"); 37 | reason = 38 | interaction.options.getString("reason") || "`No Reason Provided`"; 39 | 40 | if (target.id === interaction.member.id) 41 | return interaction.followUp({ 42 | content: `${fail} You cannot add a role to yourself`, 43 | }); 44 | 45 | if ( 46 | target.roles.highest.position >= 47 | interaction.member.roles.highest.position 48 | ) 49 | return interaction.followUp({ 50 | content: `${fail} This user is higher/equal than you`, 51 | }); 52 | 53 | if ( 54 | target.roles.highest.position >= 55 | interaction.guild.me.roles.highest.position 56 | ) 57 | return interaction.followUp({ 58 | content: `${fail} This user is higher/equal than me`, 59 | }); 60 | 61 | if (role.position >= interaction.member.roles.highest.position) 62 | return interaction.followUp({ 63 | content: `${fail} That role is higher/equal than you`, 64 | }); 65 | 66 | if (role.position >= interaction.guild.me.roles.highest.position) 67 | return interaction.followUp({ 68 | content: `${fail} That role is higher/equal than me`, 69 | }); 70 | 71 | if (target.roles.cache.has(role.id)) 72 | return interaction.followUp({ 73 | content: `${fail} User already has the provided role`, 74 | }); 75 | 76 | const embed = new MessageEmbed() 77 | .setAuthor({ 78 | name: `${interaction.user.tag}`, 79 | iconURL: interaction.user.displayAvatarURL({ 80 | dynamic: true, 81 | }), 82 | }) 83 | .setDescription( 84 | `**${interaction.user.tag}** are you sure you want to add to **${target.user.tag}** the ${role} role` 85 | ) 86 | .setFooter({ 87 | text: client.user.tag, 88 | iconURL: client.user.displayAvatarURL(), 89 | }) 90 | .setColor(randomHex()) 91 | .setTimestamp(); 92 | 93 | confirmButtons(interaction, { 94 | embed: embed, 95 | othersMessage: `Only <@${interaction.member.id}> can use these buttons`, 96 | yes: { 97 | style: "SUCCESS", 98 | label: "Add", 99 | emoji: "✔️", 100 | }, 101 | no: { 102 | style: "SECONDARY", 103 | label: "No", 104 | emoji: "🛑", 105 | }, 106 | }).then(async (confirm) => { 107 | if (confirm === "yes") { 108 | await target.roles.add(role); 109 | interaction.editReply({ 110 | content: `${success} **${role.name}** was successfully added to **${target.user.tag}**`, 111 | }); 112 | modLog(interaction, reason, { 113 | Action: "`Add Role`", 114 | Member: `${target}`, 115 | Role: `${role}`, 116 | }); 117 | } 118 | if (confirm === "no") { 119 | interaction.editReply({ 120 | content: `${fail} **${target.user.tag}** didn't get the **${role.name}** role!`, 121 | }); 122 | } 123 | if (confirm === "time") { 124 | interaction.editReply({ 125 | content: `${fail} Time is up`, 126 | }); 127 | } 128 | }); 129 | }, 130 | }; 131 | -------------------------------------------------------------------------------- /SlashCommands/Mod/warnings.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { ButtonPages } = require("leaf-utils"); // my package 3 | const { fail } = require("../../config.json"); 4 | const warnModel = require("../../models/warnModel"); 5 | 6 | module.exports = { 7 | name: "warnings", 8 | description: "display all warnings that a user has", 9 | category: "mod", 10 | options: [ 11 | { 12 | name: "target", 13 | description: "user you want to view warnings on", 14 | type: "USER", 15 | required: true, 16 | }, 17 | ], 18 | 19 | run: async (client, interaction) => { 20 | const target = interaction.options.getMember("target"); 21 | const userWarnings = await warnModel.find({ 22 | userId: target.id, 23 | guildId: interaction.guildId, 24 | }); 25 | 26 | if (!userWarnings || userWarnings.length <= 0) 27 | return interaction.followUp({ 28 | content: `${fail} ${target} has no warnings`, 29 | }); 30 | 31 | let items = []; 32 | array = []; 33 | pages = []; 34 | 35 | if (userWarnings.length > 5) { 36 | userWarnings.forEach((w, i) => { 37 | w.index = i + 1; 38 | if (items.length < 5) items.push(w); 39 | else { 40 | array.push(items); 41 | items = [w]; 42 | } 43 | }); 44 | array.push(items); 45 | 46 | array.forEach((x) => { 47 | let warns = x 48 | .map( 49 | (w) => 50 | `Moderator: ${ 51 | interaction.guild.members.cache.get( 52 | w.moderatorId 53 | ) || `${fail}` 54 | }\nReason: \`${w.reason}\`\nDate: \nWarnID: \`${w._id}\`` 57 | ) 58 | .join("\n\n"); 59 | 60 | let emb = new MessageEmbed() 61 | .setAuthor({ 62 | name: `${target.user.tag}` + "'s warnings", 63 | iconURL: target.user.displayAvatarURL({ 64 | dynamic: true, 65 | }), 66 | }) 67 | .setDescription(`${warns}`) 68 | .setFooter({ 69 | text: client.user.tag, 70 | iconURL: client.user.displayAvatarURL(), 71 | }) 72 | .setColor("#F36605") 73 | .setTimestamp(); 74 | pages.push(emb); 75 | }); 76 | 77 | await ButtonPages({ 78 | message: interaction, 79 | slash_command: true, 80 | pages: pages, 81 | time: 300000, 82 | back: { 83 | label: " ", 84 | style: "PRIMARY", 85 | emoji: "⬅️", 86 | }, 87 | home: { 88 | label: " ", 89 | style: "DANGER", 90 | emoji: "🏠", 91 | }, 92 | next: { 93 | label: " ", 94 | style: "PRIMARY", 95 | emoji: "➡️", 96 | }, 97 | authorOnly: "Only <@{{author}}> can use these buttons!", 98 | }); 99 | } else { 100 | let warns = userWarnings 101 | .map( 102 | (w) => 103 | `Moderator: ${ 104 | interaction.guild.members.cache.get( 105 | w.moderatorId 106 | ) || `${fail}` 107 | }\nReason: \`${w.reason}\`\nDate: \nWarnID: \`${w._id}\`` 110 | ) 111 | .join("\n\n"); 112 | 113 | let emb = new MessageEmbed() 114 | .setAuthor({ 115 | name: `${target.user.tag}` + "'s warnings", 116 | iconURL: target.user.displayAvatarURL({ 117 | dynamic: true, 118 | }), 119 | }) 120 | .setDescription(`${warns}`) 121 | .setFooter({ 122 | text: client.user.tag, 123 | iconURL: client.user.displayAvatarURL(), 124 | }) 125 | .setColor("#F36605") 126 | .setTimestamp(); 127 | 128 | interaction.followUp({ 129 | embeds: [emb], 130 | }); 131 | } 132 | }, 133 | }; 134 | -------------------------------------------------------------------------------- /SlashCommands/Mod/timeout.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const { 3 | confirmButtons, 4 | modLog, 5 | randomHex, 6 | } = require("../../handler/functions"); 7 | const { fail, success } = require("../../config.json"); 8 | const ms = require("ms"); 9 | 10 | module.exports = { 11 | name: "timeout", 12 | description: "time someone out", 13 | category: "mod", 14 | options: [ 15 | { 16 | name: "target", 17 | description: "target to timeout", 18 | type: "USER", 19 | required: true, 20 | }, 21 | { 22 | name: "time", 23 | description: "amount of time to timeout", 24 | type: "INTEGER", 25 | required: true, 26 | minValue: 1, 27 | maxValue: 60, 28 | }, 29 | { 30 | name: "unit", 31 | description: "time unit", 32 | type: "STRING", 33 | required: true, 34 | choices: [ 35 | { name: "seconds", value: "s" }, 36 | { name: "minutes", value: "m" }, 37 | { name: "hours", value: "h" }, 38 | { name: "days", value: "d" }, 39 | ], 40 | }, 41 | { 42 | name: "reason", 43 | description: "reason for this timeout", 44 | type: "STRING", 45 | required: false, 46 | }, 47 | ], 48 | 49 | run: async (client, interaction) => { 50 | let target = interaction.options.getMember("target"); 51 | reason = 52 | interaction.options.getString("reason") || "`No Reason Provided`"; 53 | time = ms( 54 | interaction.options.getInteger("time") + 55 | interaction.options.getString("unit") 56 | ); 57 | 58 | time > 2332800000 ? (time = 2332800000) : (time = time); 59 | 60 | if (target.id === interaction.member.id) 61 | return interaction.editReply({ 62 | content: `${fail} You cant timeout yourself`, 63 | }); 64 | 65 | if (target.id === interaction.guild.me.id) 66 | return interaction.editReply({ 67 | content: `${fail} You cant timeout me`, 68 | }); 69 | 70 | if (target.id === interaction.guild.ownerId) 71 | return interaction.followUp({ 72 | content: `${fail} You cannot mute the server owner`, 73 | }); 74 | 75 | if ( 76 | target.roles.highest.position >= 77 | interaction.member.roles.highest.position 78 | ) 79 | return interaction.editReply({ 80 | content: `${fail} This user is higher/equal than you`, 81 | }); 82 | 83 | if ( 84 | target.roles.highest.position >= 85 | interaction.guild.me.roles.highest.position 86 | ) 87 | return interaction.editReply({ 88 | content: `${fail} This user is higher/equal than me`, 89 | }); 90 | 91 | if (!target.moderatable) 92 | return interaction.editReply({ 93 | content: `${fail} This user can't be timed out`, 94 | }); 95 | 96 | const embed = new MessageEmbed() 97 | .setAuthor({ 98 | name: `${interaction.user.tag}`, 99 | iconURL: interaction.user.displayAvatarURL({ 100 | dynamic: true, 101 | }), 102 | }) 103 | .setDescription( 104 | `**${interaction.user.tag}** are you sure you want to timeout **${target.user.tag}**` 105 | ) 106 | .setFooter({ 107 | text: client.user.tag, 108 | iconURL: client.user.displayAvatarURL(), 109 | }) 110 | .setColor(randomHex()) 111 | .setTimestamp(); 112 | 113 | confirmButtons(interaction, { 114 | embed: embed, 115 | authorOnly: `Only <@${interaction.member.id}> can use these buttons`, 116 | yes: { 117 | style: "SUCCESS", 118 | label: "Timeout", 119 | emoji: "✔️", 120 | }, 121 | no: { 122 | style: "SECONDARY", 123 | label: "No", 124 | emoji: "🛑", 125 | }, 126 | }).then(async (confirm) => { 127 | if (confirm === "yes") { 128 | await target.timeout(time, reason); 129 | interaction.editReply({ 130 | content: `${success} **${ 131 | target.user.tag 132 | }** has been timed out for **${ms(time, { long: true })}**`, 133 | }); 134 | modLog(interaction, reason, { 135 | Action: "`Timeout`", 136 | Member: `${target}`, 137 | Time: `\`${ms(time, { long: true })}\``, 138 | }); 139 | } 140 | if (confirm === "no") { 141 | interaction.editReply({ 142 | content: `${fail} **${target.user.tag}** hasn't been timed out!`, 143 | }); 144 | } 145 | if (confirm === "time") { 146 | interaction.editReply({ 147 | content: `${fail} Time is up`, 148 | }); 149 | } 150 | }); 151 | }, 152 | }; 153 | -------------------------------------------------------------------------------- /handler/functions.js: -------------------------------------------------------------------------------- 1 | const { MessageButton, MessageActionRow, MessageEmbed } = require("discord.js"); 2 | const { fail } = require("../config.json"); 3 | const modLogModel = require("../models/modlogs"); 4 | 5 | module.exports = { 6 | confirmButtons(message, options = {}) { 7 | let yn = (st) => [ 8 | new MessageActionRow().addComponents( 9 | new MessageButton() 10 | .setStyle(options.yes.style || "SUCCESS") 11 | .setLabel(options.yes.label || "Yes") 12 | .setEmoji(options.yes.emoji || null) 13 | .setCustomId("confyes") 14 | .setDisabled(st), 15 | new MessageButton() 16 | .setStyle(options.no.style || "DANGER") 17 | .setLabel(options.no.label || "No") 18 | .setEmoji(options.no.emoji || null) 19 | .setCustomId("confno") 20 | .setDisabled(st) 21 | ), 22 | ]; 23 | 24 | if (!message.author) { 25 | return new Promise(async (res) => { 26 | await message 27 | .reply({ 28 | embeds: [options.embed], 29 | components: yn(false), 30 | }) 31 | .catch(async () => { 32 | await message.followUp({ 33 | embeds: [options.embed], 34 | components: yn(false), 35 | }); 36 | }); 37 | 38 | let msg = await message.fetchReply(); 39 | 40 | const collector = await msg.createMessageComponentCollector({ 41 | type: "BUTTON", 42 | time: 60000, 43 | }); 44 | 45 | collector.on("collect", async (i) => { 46 | if (i.user.id !== message.user.id) 47 | return i.reply({ 48 | content: 49 | options.authorOnly || 50 | "You cant use these buttons", 51 | ephemeral: true, 52 | }); 53 | 54 | await i.deferUpdate(); 55 | if (i.customId === "confyes") { 56 | collector.stop("confy"); 57 | message.editReply({ 58 | components: yn(true), 59 | }); 60 | 61 | res("yes"); 62 | } 63 | if (i.customId === "confno") { 64 | collector.stop("confn"); 65 | message.editReply({ 66 | components: yn(true), 67 | }); 68 | 69 | res("no"); 70 | } 71 | }); 72 | 73 | collector.on("end", async (i, r) => { 74 | if (r === "time") { 75 | message 76 | .editReply({ 77 | components: yn(true), 78 | }) 79 | .catch(() => {}); 80 | 81 | res("time"); 82 | } 83 | }); 84 | }); 85 | } else { 86 | return new Promise(async (res) => { 87 | let msg = await message.channel.send({ 88 | embeds: [options.embed], 89 | components: yn(false), 90 | }); 91 | 92 | let collector = msg.createMessageComponentCollector({ 93 | max: 1, 94 | componentType: "BUTTON", 95 | time: options.time * 1000 || 30000, 96 | }); 97 | 98 | collector.on("collect", async (i) => { 99 | if (i.user.id !== message.user.id) 100 | return i.reply({ 101 | content: 102 | options.authorOnly || 103 | "You cant use these buttons", 104 | ephemeral: true, 105 | }); 106 | 107 | await i.deferUpdate(); 108 | if (i.customId === "confyes") { 109 | collector.stop("confy"); 110 | i.message.edit({ 111 | components: yn(true), 112 | }); 113 | 114 | res("yes"); 115 | } 116 | if (i.customId === "confno") { 117 | collector.stop("confn"); 118 | i.message.edit({ 119 | components: yn(true), 120 | }); 121 | 122 | res("no"); 123 | } 124 | }); 125 | 126 | collector.on("end", async (i, r) => { 127 | if (r === "time") { 128 | msg.edit({ 129 | components: yn(true), 130 | }).catch(() => {}); 131 | 132 | res("time"); 133 | } 134 | }); 135 | }); 136 | } 137 | }, 138 | modLog(interaction, reason, fields = {}) { 139 | modLogModel.findOne( 140 | { 141 | Guild: interaction.guildId, 142 | }, 143 | async (err, data) => { 144 | if (err) throw err; 145 | if (!data || (data && !data.Channel)) return; 146 | 147 | const modLog = interaction.guild.channels.cache.get( 148 | data.Channel 149 | ); 150 | if ( 151 | modLog && 152 | modLog.viewable && 153 | modLog 154 | .permissionsFor(interaction.guild.me) 155 | .has(["SEND_MESSAGES", "EMBED_LINKS"]) 156 | ) { 157 | const embed = new MessageEmbed() 158 | .addField( 159 | "Moderator", 160 | `${interaction.user || `${fail}`}`, 161 | true 162 | ) 163 | .setFooter({ 164 | text: `${interaction.user.tag}`, 165 | iconURL: interaction.user.displayAvatarURL({ 166 | dynamic: true, 167 | }), 168 | }) 169 | .setTimestamp() 170 | .setColor("DARK_RED"); 171 | for (const field in fields) { 172 | embed.addField(field, fields[field], true); 173 | } 174 | embed.addField("Reason", reason); 175 | modLog.send({ 176 | embeds: [embed], 177 | }); 178 | } 179 | } 180 | ); 181 | }, 182 | randomHex() { 183 | return "#" + Math.floor(Math.random() * 16777215).toString(16); 184 | }, 185 | }; 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021-2022 notLeaf 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------