├── .env ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ └── label.yml ├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── readme ├── jb_beam.png ├── logs.png ├── mod-bot.png ├── mod-logs.png ├── mod-roles.png ├── slash-cmds.png ├── splash.png ├── timeout.png └── warns.png ├── src ├── commands │ ├── config │ │ ├── clean.js │ │ ├── roles.js │ │ ├── set.js │ │ └── unset.js │ ├── misc │ │ └── whois.js │ ├── mod │ │ ├── ban.js │ │ ├── deafen.js │ │ ├── kick.js │ │ ├── lock.js │ │ ├── mute.js │ │ ├── slowmode.js │ │ ├── timeout.js │ │ ├── unban.js │ │ ├── undeafen.js │ │ ├── unlock.js │ │ └── unmute.js │ └── warns │ │ ├── unwarn.js │ │ ├── warn.js │ │ └── warns.js ├── events │ ├── client │ │ └── ready.js │ ├── guild │ │ ├── channelCreate.js │ │ ├── channelDelete.js │ │ ├── channelUpdate.js │ │ ├── guildBanAdd.js │ │ ├── guildBanRemove.js │ │ ├── guildCreate.js │ │ ├── guildDelete.js │ │ ├── guildEventCreate.js │ │ ├── guildEventDelete.js │ │ ├── guildEventUpdate.js │ │ ├── guildEventUserAdd.js │ │ ├── guildEventUserRemove.js │ │ ├── guildMemberAdd.js │ │ ├── guildMemberRemove.js │ │ ├── guildMemberUpdate.js │ │ ├── roleCreate.js │ │ ├── roleDelete.js │ │ ├── roleUpdate.js │ │ ├── threadCreate.js │ │ ├── threadDelete.js │ │ └── threadUpdate.js │ └── interactions │ │ ├── interactionCreate.js │ │ └── slashCommands.js ├── models │ ├── Guilds.js │ └── Warns.js ├── struct │ ├── Bot.js │ ├── Event.js │ └── Interaction.js └── utils │ ├── Logger.js │ ├── Pagination.js │ └── Utils.js └── todo /.env: -------------------------------------------------------------------------------- 1 | TOKEN=bot-token 2 | MONGO=mongoDB-connection-URL 3 | CLIENT_ID=client-id -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '35 23 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v1 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v1 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v1 71 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: Labeler 9 | on: [pull_request] 10 | 11 | jobs: 12 | label: 13 | 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | pull-requests: write 18 | 19 | steps: 20 | - uses: actions/labeler@v2 21 | with: 22 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | .idea/ 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # parcel-bundler cache (https://parceljs.org/) 72 | .cache 73 | 74 | # Next.js build output 75 | .next 76 | 77 | # Nuxt.js build / generate output 78 | .nuxt 79 | dist 80 | 81 | # Gatsby files 82 | .cache/ 83 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 84 | # https://nextjs.org/blog/next-9-1#public-directory-support 85 | # public 86 | 87 | # vuepress build output 88 | .vuepress/dist 89 | 90 | # Serverless directories 91 | .serverless/ 92 | 93 | # FuseBox cache 94 | .fusebox/ 95 | 96 | # DynamoDB Local files 97 | .dynamodb/ 98 | 99 | # TernJS port file 100 | .tern-port 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 el bkr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: node index.js 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | mod-bot 4 | 5 |

6 | 7 |

8 | 9 | jetbrains 10 | Thanks to JetBrains for providing me free license for developing this project! 11 |

12 | 13 | 14 | ## Features 15 | - Multi guild! 16 | - Warn members 17 | - Add and remove bot moderators 18 | - Manage guild members 19 | - Log everything 20 | - Much more! 21 | 22 | splash 23 | 24 | ## Contributing 25 | - Want to contribute? Feel free to make any changes you want 26 | 1. Fork or clone the repo 27 | 2. Make any changes you want (You can also see `todo` file) 28 | 3. Open a pull request explaining what changes you've made 29 | 4. I ([el bkr](https://github.com/elbkr)) will review it and accept it :D 30 | 31 | ## Installation 32 | 33 | ### Requirements 34 | - Node v16 or higher 35 | 36 | ### Getting the files 37 | 1. GitHub CLI: `gh repo clone elbkr/mod-bot` 38 | 2. Download and extract the zip 39 | 3. Open with GitHub Desktop 40 | 41 | ### Creating the application 42 | 1. Create an application in [Discord Developer Portal](https://discord.com/developers/applications) 43 | 2. Create a BOT and Copy the BOT token 44 | 3. Enable all privileged gateway intents 45 | 4. Go to OAuth2 and copy the client ID 46 | 5. Paste the token at `TOKEN` line and the client ID at `CLIENT_ID` line in `.env` file 47 | 6. Change `REPLACE_THIS` in the URL below with the client ID, and enter the link in your browser 48 | 49 | https://discord.com/api/oauth2/authorize?client_id=REPLACE_THIS&permissions=8&scope=applications.commands%20bot 50 | 51 | ### Connecting to mongo DB 52 | 1. Login or register into [Mongo DB](https://account.mongodb.com/account/login) 53 | 2. Create a cluster and complete the configuration 54 | 3. Get the connection url by pressing on `connect < connect your application` 55 | 4. Replace the `password` with your database access password 56 | 5. Paste the URL into `.env` file at `MONGO` line 57 | 58 | *The URL looks like this:* `mongodb+srv://username:password@clusterName.pjxpv.mongodb.net/MyFirstDatabase?retryWrites=true&w=majority` 59 | 60 | ### .ENV Output 61 | After the configuration, the `.env` file should look like this: 62 | ```env 63 | TOKEN=SuPerReALToken.BelIeVe_Me_itS_ReaL 64 | MONGO=mongodb+srv://username:password@clusterName.pjxpv.mongodb.net/MyFirstDatabase?retryWrites=true&w=majority 65 | CLIENT_ID=521311050193436682 66 | ``` 67 | 68 | ### Running the BOT 69 | 1. Open a terminal and run `npm install` or `npm i` 70 | 2. Run `node .` 71 | 72 | ## Free hosting 73 | 1. Local host 74 | After the configuration, run `node .` in the terminal (not recommended) 75 | - It turns off when you turn off the PC 76 | 2. Heroku 77 | - After the configuration, add the files to a GitHub repository 78 | - Login or register in [Heroku](https://id.heroku.com/login) 79 | - Create a new app 80 | - In `deploy` section, press `Connect to GitHub` 81 | - After connecting, search for the repository and press `connect` 82 | - Press **Enable automatic deploys** (optional) 83 | - Click **Deploy Branch** 84 | - Go to `Resources` section 85 | - Disable the `web` type and enable `worker` type 86 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); //{path: "dev.env"} if developing 2 | 3 | /* Import our client structure */ 4 | const Bot = require("./src/struct/Bot"); 5 | const client = new Bot(); 6 | 7 | /* Call our start function to load the bot instance */ 8 | (async () => await client.start(process.env.TOKEN))(); 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mod-bot", 3 | "version": "1.0.0", 4 | "description": "discord.js moderation bot using mongoDB", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/elbkr/mod-bot.git" 12 | }, 13 | "keywords": [ 14 | "discord-bot", 15 | "discord.js", 16 | "discord", 17 | "moderation", 18 | "moderation-bot", 19 | "mongoDB" 20 | ], 21 | "author": "el bkr (https://github.com/elbkr)", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/elbkr/mod-bot/issues" 25 | }, 26 | "homepage": "https://github.com/elbkr/mod-bot#readme", 27 | "dependencies": { 28 | "@discordjs/opus": "^0.7.0", 29 | "@discordjs/rest": "^0.2.0-canary.0", 30 | "bufferutil": "^4.0.6", 31 | "chalk": "^4.0.0", 32 | "discord-api-types": "^0.26.0", 33 | "discord.js": "^13.4.0", 34 | "dotenv": "^10.0.0", 35 | "fs": "^0.0.1-security", 36 | "glob": "^7.2.0", 37 | "moment": "^2.29.1", 38 | "mongoose": "^6.1.3", 39 | "ms": "^2.1.3", 40 | "utf-8-validate": "^5.0.7", 41 | "util": "^0.12.4" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /readme/jb_beam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/jb_beam.png -------------------------------------------------------------------------------- /readme/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/logs.png -------------------------------------------------------------------------------- /readme/mod-bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/mod-bot.png -------------------------------------------------------------------------------- /readme/mod-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/mod-logs.png -------------------------------------------------------------------------------- /readme/mod-roles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/mod-roles.png -------------------------------------------------------------------------------- /readme/slash-cmds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/slash-cmds.png -------------------------------------------------------------------------------- /readme/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/splash.png -------------------------------------------------------------------------------- /readme/timeout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/timeout.png -------------------------------------------------------------------------------- /readme/warns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elbkr/mod-bot/ec54ab0a936058ab8325c852d27b0f09848fdf16/readme/warns.png -------------------------------------------------------------------------------- /src/commands/config/clean.js: -------------------------------------------------------------------------------- 1 | module.exports = class Clean extends Interaction { 2 | constructor() { 3 | super({ 4 | name: "clean", 5 | description: "Deletes bot messages in the channel", 6 | options: [ 7 | { 8 | type: "4", 9 | name: "amount", 10 | description: "The amount of messages to delete", 11 | required: true 12 | }, 13 | { 14 | type: "3", 15 | name: "filter", 16 | description: "The filter to use", 17 | required: false, 18 | choices: [ 19 | { 20 | name: "Bots", 21 | value: "bots" 22 | }, 23 | { 24 | name: "Bot (Me)", 25 | value: "me" 26 | }, 27 | { 28 | name: "Users", 29 | value: "users" 30 | } 31 | ] 32 | } 33 | 34 | ], 35 | }); 36 | } 37 | async exec(int, data) { 38 | if (!int.member.permissions.has("MANAGE_GUILD")) 39 | return int.reply({ 40 | content: "You don't have the required permissions to do this!", 41 | ephemeral: true, 42 | }); 43 | 44 | let amount = int.options.getInteger("amount") 45 | let filter = int.options.getString("filter") 46 | 47 | if (amount > 100) { 48 | return int.reply({ 49 | content: "You can't delete more than 100 messages at the same time!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | let messages = await int.channel.messages.fetch() 55 | let deleted = 0 56 | messages.forEach( m => { 57 | if(deleted >= amount) return 58 | 59 | if(filter) { 60 | if(filter === "bots") { 61 | if(m.author.bot) { 62 | if(m.deletable) { 63 | m.delete().catch(err => console.log(err)) 64 | deleted += 1 65 | } 66 | } 67 | } 68 | if(filter === "me") { 69 | if(m.author.id === int.client.user.id) { 70 | if(m.deletable) { 71 | m.delete().catch(err => console.log(err)) 72 | deleted += 1 73 | } 74 | } 75 | } 76 | if(filter === "users") { 77 | if(!m.author.bot) { 78 | if(m.deletable) { 79 | m.delete().catch(err => console.log(err)) 80 | deleted += 1 81 | } 82 | } 83 | } 84 | } else { 85 | if(m.deletable) { 86 | m.delete().catch(err => console.log(err)) 87 | deleted += 1 88 | } 89 | 90 | } 91 | 92 | }) 93 | 94 | return int.reply({ 95 | content: "Succesfully deleted messages!", 96 | ephemeral: true, 97 | }) 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /src/commands/config/roles.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Roles extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "roles", 7 | description: "Manage mod roles", 8 | options: [ 9 | { 10 | type: "1", 11 | name: "add", 12 | description: "Add a role to the mod roles list", 13 | options: [ 14 | { 15 | type: "8", 16 | name: "role", 17 | description: "The role to add", 18 | required: true, 19 | }, 20 | ] 21 | }, 22 | { 23 | type: "1", 24 | name: "remove", 25 | description: "Remove a role from the mod roles list", 26 | options: [ 27 | { 28 | type: "8", 29 | name: "role", 30 | description: "The role to remove", 31 | required: true, 32 | }, 33 | ] 34 | }, 35 | { 36 | type: "1", 37 | name: "list", 38 | description: "List all mod roles", 39 | }, 40 | ], 41 | }); 42 | } 43 | async exec(int, data) { 44 | if (!int.member.permissions.has("MANAGE_GUILD")) 45 | return int.reply({ 46 | content: "You don't have the required permissions to do this!", 47 | ephemeral: true, 48 | }); 49 | 50 | const cmd = int.options.getSubcommand() 51 | 52 | if(cmd === "add") { 53 | 54 | let role = int.options._hoistedOptions[0].role 55 | 56 | if(role.id === int.guild.id) { 57 | return int.reply({ 58 | content: "The *everyone* role is not manageable!", 59 | ephemeral: true, 60 | }); 61 | } 62 | let old = data.modRoles.find((r) => r === role.id); 63 | 64 | if (old) { 65 | return int.reply({ 66 | content: `The role ${role} is already in the list!`, 67 | ephemeral: true, 68 | }); 69 | } 70 | 71 | data.modRoles.push(role.id); 72 | await data.save(); 73 | 74 | return int.reply({ 75 | content: `Added role ${role} to the mod roles list!`, 76 | ephemeral: true, 77 | }); 78 | } 79 | if (cmd === "remove") { 80 | let role = int.options._hoistedOptions[0].role; 81 | 82 | if (role.id === int.guild.id) { 83 | return int.reply({ 84 | content: "The *everyone* role is not manageable!", 85 | ephemeral: true, 86 | }); 87 | } 88 | 89 | let old = data.modRoles.find((r) => r === role.id); 90 | 91 | if (!old) 92 | return int.reply({ 93 | content: `The role ${role} is not in the list!`, 94 | ephemeral: true, 95 | }); 96 | 97 | let index = data.modRoles.indexOf(role.id); 98 | data.modRoles.splice(index, 1); 99 | await data.save(); 100 | 101 | return int.reply({ 102 | content: `Removed role ${role} from the mod roles list!`, 103 | ephemeral: true, 104 | }); 105 | } 106 | if (cmd === "list") { 107 | let mods = data.modRoles; 108 | 109 | if (!mods.length) 110 | return int.reply({ 111 | content: "There are no mod roles set!", 112 | ephemeral: true, 113 | }); 114 | 115 | let emb = new MessageEmbed() 116 | .setTitle("Mod roles list") 117 | .setThumbnail(int.guild.iconURL({ size: 2048, dynamic: true })) 118 | .setColor("#2f3136") 119 | .setDescription(`${mods.map((m) => `<@&${m}>`).join(" ")}`) 120 | .setTimestamp(); 121 | 122 | return int.reply({ embeds: [emb] }); 123 | } 124 | } 125 | }; 126 | -------------------------------------------------------------------------------- /src/commands/config/set.js: -------------------------------------------------------------------------------- 1 | module.exports = class SetFunction extends Interaction { 2 | constructor() { 3 | super({ 4 | name: "set", 5 | description: "Set a config value", 6 | options: [ 7 | { 8 | type: "3", 9 | name: "function", 10 | description: "The function to set", 11 | required: true, 12 | choices: [ 13 | { 14 | name: "Server logs channel", 15 | value: "logs", 16 | }, 17 | { 18 | name: "Moderation logs channel", 19 | value: "mod-logs", 20 | }, 21 | ], 22 | }, 23 | { 24 | type: "7", 25 | name: "channel", 26 | description: "The channel or category to set the function to", 27 | required: true, 28 | }, 29 | ], 30 | }); 31 | } 32 | async exec(int, data) { 33 | const option = int.options.getString("function"); 34 | const channel = int.options.getChannel("channel"); 35 | 36 | if (!int.member.permissions.has("MANAGE_GUILD")) { 37 | return int.reply({ 38 | content: "You don't have permission to do that!", 39 | ephemeral: true, 40 | }); 41 | } 42 | 43 | if (channel.type === "GUILD_CATEGORY") { 44 | return int.reply({ 45 | content: "The channel type must be a text channel!", 46 | ephemeral: true, 47 | }); 48 | } else if (channel.type === "GUILD_VOICE") { 49 | return int.reply({ 50 | content: "The channel type must be a text channel!", 51 | ephemeral: true, 52 | }); 53 | } 54 | 55 | 56 | if (option === "logs") { 57 | data.logsChannel = channel.id; 58 | await data.save(); 59 | 60 | return int.reply({ 61 | content: `Set the logs channel to ${channel}`, 62 | ephemeral: true, 63 | }); 64 | } 65 | if (option === "mod-logs") { 66 | data.modLogs = channel.id; 67 | await data.save(); 68 | 69 | return int.reply({ 70 | content: `Set the moderation logs channel to ${channel}`, 71 | ephemeral: true, 72 | }); 73 | } 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /src/commands/config/unset.js: -------------------------------------------------------------------------------- 1 | module.exports = class Unset extends Interaction { 2 | constructor() { 3 | super({ 4 | name: "unset", 5 | description: "Unset a config value", 6 | options: [ 7 | { 8 | type: "3", 9 | name: "function", 10 | description: "The function to unset", 11 | required: true, 12 | choices: [ 13 | { 14 | name: "Server logs channel", 15 | value: "logs", 16 | }, 17 | { 18 | name: "Moderation logs channel", 19 | value: "mod-logs", 20 | }, 21 | ], 22 | }, 23 | ], 24 | }); 25 | } 26 | async exec(int, data) { 27 | const option = int.options.getString("function"); 28 | 29 | if (!int.member.permissions.has("MANAGE_GUILD")) { 30 | return int.reply({ 31 | content: "You don't have permission to do that!", 32 | ephemeral: true, 33 | }); 34 | } 35 | 36 | if (option === "logs") { 37 | data.logsChannel = null; 38 | await data.save(); 39 | 40 | return int.reply({ 41 | content: "Logs channel unset!", 42 | ephemeral: true, 43 | }); 44 | } 45 | if (option === "mod-logs") { 46 | data.modLogs = null; 47 | await data.save(); 48 | 49 | return int.reply({ 50 | content: "Mod logs channel unset!", 51 | ephemeral: true, 52 | }); 53 | } 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /src/commands/misc/whois.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const moment = require("moment"); 3 | 4 | module.exports = class Whois extends Interaction { 5 | constructor() { 6 | super({ 7 | name: "whois", 8 | description: "Get information about a user", 9 | options: [ 10 | { 11 | type: 6, 12 | name: "user", 13 | description: "The user to get information about", 14 | required: true, 15 | }, 16 | ], 17 | }); 18 | } 19 | async exec(int) { 20 | let member = int.options.getMember("user"); 21 | 22 | //NOW BADGES 23 | let flags = await member.user.flags; 24 | flags = await flags.toArray(); 25 | 26 | let badges = []; 27 | 28 | flags.forEach((f) => { 29 | if (f.toLowerCase().includes("bravery")) f = "HOUSE OF BRAVERY"; 30 | if (f.toLowerCase().includes("brilliance")) f = "HOUSE OF BRILLIANCE"; 31 | if (f.toLowerCase().includes("balance")) f = "HOUSE OF BALANCE"; 32 | badges.push(f.replace("_", " ")); 33 | }); 34 | 35 | // ROLES 36 | let roles = 37 | member.roles.cache.filter((r) => r.id !== int.guild.id).size > 0 38 | ? member.roles.cache 39 | .filter((r) => r.id !== int.guild.id) 40 | .map((x) => x) 41 | .join(" ") 42 | : "None"; 43 | 44 | let embed = new MessageEmbed() 45 | .setColor("#2f3136") 46 | .setThumbnail(member.user.displayAvatarURL({ dynamic: true })); 47 | 48 | embed.setAuthor( 49 | member.user.tag, 50 | member.user.displayAvatarURL({ dynamic: true }) 51 | ); 52 | 53 | if (member.nickname) embed.addField("Nickname", member.nickname); 54 | 55 | embed 56 | .addField("Joined at", moment(member.user.joinedAt).format("LLLL")) 57 | .addField( 58 | "Created at", 59 | moment(member.user.createdAt).format("LLLL") 60 | ) 61 | .addField("Badges", badges.join(" | ") || "None") 62 | 63 | .addField("Roles", roles, true) 64 | .setFooter(`ID: ${member.user.id} `); 65 | 66 | return int.reply({ embeds: [embed] }); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /src/commands/mod/ban.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Ban extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "ban", 7 | description: "Bans a user from the server", 8 | options: [ 9 | { 10 | type: "6", 11 | name: "user", 12 | description: "The user to ban", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the ban", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("BAN_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let member = int.options.getMember("user") 35 | let reason = int.options.getString("reason") 36 | 37 | if(member.user.id === int.user.id) { 38 | return int.reply({ 39 | content: "You can't moderate yourself!", 40 | ephemeral: true, 41 | }); 42 | } else if(member.user.id === int.client.user.id) { 43 | return int.reply({ 44 | content: "You can't moderate me!", 45 | ephemeral: true, 46 | }); 47 | } else if(member.user.id === int.guild.ownerId) { 48 | return int.reply({ 49 | content: "You can't moderate the server owner!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't ban someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | 62 | if(!member.bannable) { 63 | return int.reply({ 64 | content: "I can't ban that user!", 65 | ephemeral: true, 66 | }); 67 | } 68 | 69 | await member.ban({reason: `${reason}`}).then(async () => { 70 | if(data.modLogs) { 71 | let modChannel = await int.guild.channels.fetch(data.modLogs); 72 | if(modChannel) { 73 | let embed = new MessageEmbed() 74 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 75 | .setTitle("User banned") 76 | .setColor("#2f3136") 77 | .setDescription(`Reason: ${reason}`) 78 | .addFields( 79 | {name: "User", value: `${member}`, inline: true}, 80 | {name: "Moderator", value:`${int.member}`, inline: true}, 81 | ) 82 | .setTimestamp(); 83 | modChannel.send({embeds: [embed]}); 84 | } 85 | } 86 | 87 | return int.reply({ 88 | content: `${member} has been banned! Reason: **${reason}**`, 89 | ephemeral: true, 90 | }); 91 | }); 92 | 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /src/commands/mod/deafen.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Deafen extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "deafen", 7 | description: "Deafens a user in a voice channel", 8 | options: [ 9 | { 10 | type: "6", 11 | name: "user", 12 | description: "The user to deafen", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the deafen", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("DEAFEN_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let member = int.options.getMember("user") 35 | let reason = int.options.getString("reason") 36 | 37 | if(member.user.id === int.user.id) { 38 | return int.reply({ 39 | content: "You can't moderate yourself!", 40 | ephemeral: true, 41 | }); 42 | } else if(member.user.id === int.client.user.id) { 43 | return int.reply({ 44 | content: "You can't moderate me!", 45 | ephemeral: true, 46 | }); 47 | } else if(member.user.id === int.guild.ownerId) { 48 | return int.reply({ 49 | content: "You can't moderate the server owner!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't deafen someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | 62 | if(!member.manageable) { 63 | return int.reply({ 64 | content: "I can't deafen that user!", 65 | ephemeral: true, 66 | }); 67 | } 68 | 69 | if(!member.voice.channel) { 70 | return int.reply({ 71 | content: "That user isn't in a voice channel!", 72 | ephemeral: true, 73 | }); 74 | } 75 | 76 | if(member.voice.selfDeaf || member.voice.deaf) { 77 | return int.reply({ 78 | content: "That user is already deafened!", 79 | ephemeral: true, 80 | }); 81 | } 82 | 83 | await member.voice.setDeaf(true, reason).then(async () => { 84 | if(data.modLogs) { 85 | let modChannel = await int.guild.channels.fetch(data.modLogs); 86 | if(modChannel) { 87 | let embed = new MessageEmbed() 88 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 89 | .setTitle("User deafened") 90 | .setColor("#2f3136") 91 | .setDescription(`Reason: ${reason}`) 92 | .addFields( 93 | {name: "User", value: `${member}`, inline: true}, 94 | {name: "Moderator", value:`${int.member}`, inline: true}, 95 | ) 96 | .setTimestamp(); 97 | modChannel.send({embeds: [embed]}); 98 | } 99 | } 100 | 101 | return int.reply({ 102 | content: `${member} has been deafened! Reason: **${reason}**`, 103 | ephemeral: true, 104 | }); 105 | }); 106 | 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /src/commands/mod/kick.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Kick extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "kick", 7 | description: "Kicks a user from the server", 8 | options: [ 9 | { 10 | type: "6", 11 | name: "user", 12 | description: "The user to kick", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the kick", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("KICK_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let member = int.options.getMember("user") 35 | let reason = int.options.getString("reason") 36 | 37 | if(member.user.id === int.user.id) { 38 | return int.reply({ 39 | content: "You can't moderate yourself!", 40 | ephemeral: true, 41 | }); 42 | } else if(member.user.id === int.client.user.id) { 43 | return int.reply({ 44 | content: "You can't moderate me!", 45 | ephemeral: true, 46 | }); 47 | } else if(member.user.id === int.guild.ownerId) { 48 | return int.reply({ 49 | content: "You can't moderate the server owner!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't kick someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | 62 | if(!member.kickable) { 63 | return int.reply({ 64 | content: "I can't kick that user!", 65 | ephemeral: true, 66 | }); 67 | } 68 | 69 | await member.kick({reason: `${reason}`}).then(async () => { 70 | if(data.modLogs) { 71 | let modChannel = await int.guild.channels.fetch(data.modLogs); 72 | if(modChannel) { 73 | let embed = new MessageEmbed() 74 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 75 | .setTitle("User kicked") 76 | .setColor("#2f3136") 77 | .setDescription(`Reason: ${reason}`) 78 | .addFields( 79 | {name: "User", value: `${member}`, inline: true}, 80 | {name: "Moderator", value:`${int.member}`, inline: true}, 81 | ) 82 | .setTimestamp(); 83 | modChannel.send({embeds: [embed]}); 84 | } 85 | } 86 | 87 | return int.reply({ 88 | content: `${member} has been kicked! Reason: **${reason}**`, 89 | ephemeral: true, 90 | }); 91 | }); 92 | 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /src/commands/mod/lock.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Lock extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "lock", 7 | description: "Locks a channel", 8 | options: [ 9 | { 10 | type: "7", 11 | name: "channel", 12 | description: "The channel to lock", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the lock", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("MANAGE_CHANNELS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let channel = int.options.getChannel("channel"); 35 | let reason = int.options.getString("reason") 36 | 37 | if(!channel.permissionsFor(int.guild.roles.everyone).has("SEND_MESSAGES")) { 38 | return int.reply({ 39 | content: "That channel is already locked!", 40 | ephemeral: true, 41 | }); 42 | } 43 | 44 | await channel.edit({ 45 | permissionOverwrites: [ 46 | { 47 | id: int.guild.id, 48 | deny: ["SEND_MESSAGES"], 49 | }, 50 | ] 51 | }, reason).then(async (c) => { 52 | if(data.modLogs) { 53 | let modChannel = await int.guild.channels.fetch(data.modLogs); 54 | if(modChannel) { 55 | let embed = new MessageEmbed() 56 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 57 | .setTitle("Channel locked") 58 | .setColor("#2f3136") 59 | .setDescription(`Reason: ${reason}`) 60 | .addFields( 61 | {name: "Channel", value: `${c}`, inline: true}, 62 | {name: "Moderator", value:`${int.member}`, inline: true}, 63 | ) 64 | .setTimestamp(); 65 | modChannel.send({embeds: [embed]}); 66 | } 67 | } 68 | 69 | return int.reply({ 70 | content: `${channel} has been locked! Reason: **${reason}**`, 71 | ephemeral: true, 72 | }); 73 | }); 74 | 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /src/commands/mod/mute.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Mute extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "mute", 7 | description: "Mutes a user in a voice channel", 8 | options: [ 9 | { 10 | type: "6", 11 | name: "user", 12 | description: "The user to mute", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the mute", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("MUTE_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let member = int.options.getMember("user") 35 | let reason = int.options.getString("reason") 36 | 37 | if(member.user.id === int.user.id) { 38 | return int.reply({ 39 | content: "You can't moderate yourself!", 40 | ephemeral: true, 41 | }); 42 | } else if(member.user.id === int.client.user.id) { 43 | return int.reply({ 44 | content: "You can't moderate me!", 45 | ephemeral: true, 46 | }); 47 | } else if(member.user.id === int.guild.ownerId) { 48 | return int.reply({ 49 | content: "You can't moderate the server owner!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't mute someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | 62 | if(!member.manageable) { 63 | return int.reply({ 64 | content: "I can't mute that user!", 65 | ephemeral: true, 66 | }); 67 | } 68 | 69 | if(!member.voice.channel) { 70 | return int.reply({ 71 | content: "That user isn't in a voice channel!", 72 | ephemeral: true, 73 | }); 74 | } 75 | 76 | if(member.voice.selfMute || member.voice.mute) { 77 | return int.reply({ 78 | content: "That user is already muted!", 79 | ephemeral: true, 80 | }); 81 | } 82 | 83 | await member.voice.setMute(true, reason).then(async () => { 84 | if(data.modLogs) { 85 | let modChannel = await int.guild.channels.fetch(data.modLogs); 86 | if(modChannel) { 87 | let embed = new MessageEmbed() 88 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 89 | .setTitle("User muted") 90 | .setColor("#2f3136") 91 | .setDescription(`Reason: ${reason}`) 92 | .addFields( 93 | {name: "User", value: `${member}`, inline: true}, 94 | {name: "Moderator", value:`${int.member}`, inline: true}, 95 | ) 96 | .setTimestamp(); 97 | modChannel.send({embeds: [embed]}); 98 | } 99 | } 100 | 101 | return int.reply({ 102 | content: `${member} has been muted! Reason: **${reason}**`, 103 | ephemeral: true, 104 | }); 105 | }); 106 | 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /src/commands/mod/slowmode.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Slowmode extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "slowmode", 7 | description: "Changes the slowmode of a text channel", 8 | options: [ 9 | { 10 | type: "7", 11 | name: "channel", 12 | description: "The channel to change the slowmode of", 13 | required: true 14 | }, 15 | { 16 | type: "4", 17 | name: "time", 18 | description: "The time in seconds to set the slowmode to", 19 | required: true 20 | }, 21 | { 22 | type: "3", 23 | name: "reason", 24 | description: "The reason for the change", 25 | required: true 26 | } 27 | ], 28 | }); 29 | } 30 | async exec(int, data) { 31 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 32 | 33 | if (!isMod && !int.member.permissions.has("MANAGE_CHANNELS") ) { 34 | return int.reply({ 35 | content: "You don't have permission to do that!", 36 | ephemeral: true, 37 | }); 38 | } 39 | 40 | let channel = int.options.getChannel("channel"); 41 | let time = int.options.getInteger("time"); 42 | let reason = int.options.getString("reason") 43 | 44 | if(time < 0) { 45 | return int.reply({ 46 | content: "The time must be greater than 0!", 47 | ephemeral: true, 48 | }); 49 | } else if(time > 21600) { 50 | return int.reply({ 51 | content: "The time must be less than 6 hours!", 52 | ephemeral: true, 53 | }); 54 | } 55 | 56 | await channel.setRateLimitPerUser(time, reason).then(async (c) => { 57 | if(data.modLogs) { 58 | let modChannel = await int.guild.channels.fetch(data.modLogs); 59 | if(modChannel) { 60 | let embed = new MessageEmbed() 61 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 62 | .setTitle("Channel slow mode changed") 63 | .setColor("#2f3136") 64 | .setDescription(`Reason: ${reason}`) 65 | .addFields( 66 | {name: "Channel", value: `${c}`, inline: true}, 67 | {name: "Time", value: `${time} seconds`, inline: true}, 68 | {name: "Moderator", value:`${int.member}`, inline: true}, 69 | ) 70 | .setTimestamp(); 71 | modChannel.send({embeds: [embed]}); 72 | } 73 | } 74 | 75 | return int.reply({ 76 | content: `Slow mode of ${channel} set to ${time} seconds! Reason: **${reason}**`, 77 | ephemeral: true, 78 | }); 79 | }); 80 | 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /src/commands/mod/timeout.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | const ms = require('ms'); 3 | 4 | module.exports = class TimeOut extends Interaction { 5 | constructor() { 6 | super({ 7 | name: "timeout", 8 | description: "Timeouts a user", 9 | options: [ 10 | { 11 | type: "6", 12 | name: "user", 13 | description: "The user to timeout", 14 | required: true 15 | }, 16 | { 17 | type: "3", 18 | name: "length", 19 | description: "The length of the timeout", 20 | required: true 21 | }, 22 | { 23 | type: "3", 24 | name: "reason", 25 | description: "The reason for the timeout", 26 | required: true 27 | } 28 | ], 29 | }); 30 | } 31 | async exec(int, data) { 32 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 33 | 34 | if (!isMod && !int.member.permissions.has("MODERATE_MEMBERS")) { 35 | return int.reply({ 36 | content: "You don't have permission to do that!", 37 | ephemeral: true, 38 | }); 39 | } 40 | 41 | let member = int.options.getMember("user") 42 | let length = int.options.getString("length") 43 | let reason = int.options.getString("reason") 44 | 45 | 46 | if(member.user.id === int.user.id) { 47 | return int.reply({ 48 | content: "You can't moderate yourself!", 49 | ephemeral: true, 50 | }); 51 | } else if(member.user.id === int.client.user.id) { 52 | return int.reply({ 53 | content: "You can't moderate me!", 54 | ephemeral: true, 55 | }); 56 | } else if(member.user.id === int.guild.ownerId) { 57 | return int.reply({ 58 | content: "You can't moderate the server owner!", 59 | ephemeral: true, 60 | }); 61 | } 62 | 63 | if(int.member.roles.highest.position < member.roles.highest.position) { 64 | return int.reply({ 65 | content: "You can't timeout someone with a higher role than you!", 66 | ephemeral: true, 67 | }); 68 | } 69 | 70 | 71 | if(member.communicationDisabledUntilTimestamp > 0) { 72 | return int.reply({ 73 | content: "That user is already timed out!", 74 | ephemeral: true, 75 | }); 76 | } 77 | 78 | if(!member.moderatable || !member.manageable) { 79 | return int.reply({ 80 | content: "I can't timeout that user!", 81 | ephemeral: true, 82 | }); 83 | } 84 | 85 | 86 | let time = ms(length); 87 | 88 | if(!time) { 89 | return int.reply({ 90 | content: "Enter a valid time!", 91 | ephemeral: true, 92 | }); 93 | } 94 | 95 | if(time > 2.419e+9) { 96 | return int.reply({ 97 | content: "You can't timeout someone more than **28 days**!", 98 | ephemeral: true, 99 | }); 100 | } else if(time < 1000) { 101 | return int.reply({ 102 | content: "You can't timeout someone less than **1 second**!", 103 | ephemeral: true, 104 | }); 105 | } 106 | 107 | await member.timeout(time, reason).then(async () => { 108 | if(data.modLogs) { 109 | let modChannel = await int.guild.channels.fetch(data.modLogs); 110 | if(modChannel) { 111 | let embed = new MessageEmbed() 112 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 113 | .setTitle("User Timed out") 114 | .setColor("#2f3136") 115 | .setDescription(`Reason: ${reason}`) 116 | .addFields( 117 | {name: "User", value: `${member}`, inline: true}, 118 | {name: "Moderator", value:`${int.member}`, inline: true}, 119 | {name: "Time", value: `${length}`, inline: false} 120 | ) 121 | .setTimestamp(); 122 | modChannel.send({embeds: [embed]}); 123 | } 124 | } 125 | 126 | return int.reply({ 127 | content: `${member} has been timed out for ${length}! Reason: **${reason}**`, 128 | ephemeral: true, 129 | }); 130 | }); 131 | 132 | } 133 | }; 134 | -------------------------------------------------------------------------------- /src/commands/mod/unban.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Unban extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "unban", 7 | description: "Unbans a user from the server", 8 | options: [ 9 | { 10 | type: "3", 11 | name: "user", 12 | description: "The ID of the user to unban", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the unban", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("BAN_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let id = int.options.getString("user") 35 | let reason = int.options.getString("reason") 36 | 37 | 38 | let userIdRegex = /^[0-9]{17,18}$/; 39 | if(!userIdRegex.test(id)) { 40 | return int.reply({ 41 | content: "Provide a valid user ID!", 42 | ephemeral: true, 43 | }); 44 | } 45 | 46 | if(id === int.client.user.id) { 47 | return int.reply({ 48 | content: "You can't moderate me!", 49 | ephemeral: true, 50 | }); 51 | }else if(id === int.guild.ownerId) { 52 | return int.reply({ 53 | content: "You can't moderate the server owner!", 54 | ephemeral: true, 55 | }); 56 | } 57 | 58 | let isBan = await int.guild.bans.fetch(id); 59 | 60 | if(!isBan) { 61 | return int.reply({ 62 | content: "That user is not banned!", 63 | ephemeral: true, 64 | }); 65 | } 66 | 67 | await int.guild.members.unban(isBan.user, `${reason}`).then(async () => { 68 | if(data.modLogs) { 69 | let modChannel = await int.guild.channels.fetch(data.modLogs); 70 | if(modChannel) { 71 | let embed = new MessageEmbed() 72 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 73 | .setTitle("User Unbanned") 74 | .setColor("#2f3136") 75 | .setDescription(`Reason: ${reason}`) 76 | .addFields( 77 | {name: "User", value: `${isBan.user}`, inline: true}, 78 | {name: "Moderator", value:`${int.member}`, inline: true}, 79 | ) 80 | .setTimestamp(); 81 | modChannel.send({embeds: [embed]}); 82 | } 83 | } 84 | 85 | return int.reply({ 86 | content: `${isBan.user} has been unbanned! Reason: **${reason}**`, 87 | ephemeral: true, 88 | }); 89 | }); 90 | 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /src/commands/mod/undeafen.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Undeafen extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "undeafen", 7 | description: "Undeafens a user in a voice channel", 8 | options: [ 9 | { 10 | type: "6", 11 | name: "user", 12 | description: "The user to undeafen", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the undeafen", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("DEAFEN_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let member = int.options.getMember("user") 35 | let reason = int.options.getString("reason") 36 | 37 | if(member.user.id === int.user.id) { 38 | return int.reply({ 39 | content: "You can't moderate yourself!", 40 | ephemeral: true, 41 | }); 42 | } else if(member.user.id === int.client.user.id) { 43 | return int.reply({ 44 | content: "You can't moderate me!", 45 | ephemeral: true, 46 | }); 47 | } else if(member.user.id === int.guild.ownerId) { 48 | return int.reply({ 49 | content: "You can't moderate the server owner!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't undeafen someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | 62 | if(!member.manageable) { 63 | return int.reply({ 64 | content: "I can't undeafen that user!", 65 | ephemeral: true, 66 | }); 67 | } 68 | 69 | if(!member.voice.channel) { 70 | return int.reply({ 71 | content: "That user isn't in a voice channel!", 72 | ephemeral: true, 73 | }); 74 | } 75 | 76 | if(!member.voice.selfDeaf && !member.voice.deaf) { 77 | return int.reply({ 78 | content: "That user isn't deafened!", 79 | ephemeral: true, 80 | }); 81 | } 82 | 83 | await member.voice.setDeaf(false, reason).then(async () => { 84 | if(data.modLogs) { 85 | let modChannel = await int.guild.channels.fetch(data.modLogs); 86 | if(modChannel) { 87 | let embed = new MessageEmbed() 88 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 89 | .setTitle("User undeafened") 90 | .setColor("#2f3136") 91 | .setDescription(`Reason: ${reason}`) 92 | .addFields( 93 | {name: "User", value: `${member}`, inline: true}, 94 | {name: "Moderator", value:`${int.member}`, inline: true}, 95 | ) 96 | .setTimestamp(); 97 | modChannel.send({embeds: [embed]}); 98 | } 99 | } 100 | 101 | return int.reply({ 102 | content: `${member} has been undeafened! Reason: **${reason}**`, 103 | ephemeral: true, 104 | }); 105 | }); 106 | 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /src/commands/mod/unlock.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Unlock extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "unlock", 7 | description: "Unlocks a channel", 8 | options: [ 9 | { 10 | type: "7", 11 | name: "channel", 12 | description: "The channel to unlock", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the unlock", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("MANAGE_CHANNELS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let channel = int.options.getChannel("channel"); 35 | let reason = int.options.getString("reason") 36 | 37 | 38 | if(channel.permissionsFor(int.guild.roles.everyone).has("SEND_MESSAGES")) { 39 | return int.reply({ 40 | content: "That channel is already unlocked!", 41 | ephemeral: true, 42 | }); 43 | } 44 | 45 | await channel.edit({ 46 | permissionOverwrites: [ 47 | { 48 | id: int.guild.id, 49 | allow: ["SEND_MESSAGES"], 50 | }, 51 | ] 52 | }, reason).then(async (c) => { 53 | if(data.modLogs) { 54 | let modChannel = await int.guild.channels.fetch(data.modLogs); 55 | if(modChannel) { 56 | let embed = new MessageEmbed() 57 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 58 | .setTitle("Channel unlocked") 59 | .setColor("#2f3136") 60 | .setDescription(`Reason: ${reason}`) 61 | .addFields( 62 | {name: "Channel", value: `${c}`, inline: true}, 63 | {name: "Moderator", value:`${int.member}`, inline: true}, 64 | ) 65 | .setTimestamp(); 66 | modChannel.send({embeds: [embed]}); 67 | } 68 | } 69 | 70 | return int.reply({ 71 | content: `${channel} has been unlocked! Reason: **${reason}**`, 72 | ephemeral: true, 73 | }); 74 | }); 75 | 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /src/commands/mod/unmute.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require('discord.js'); 2 | 3 | module.exports = class Unmute extends Interaction { 4 | constructor() { 5 | super({ 6 | name: "unmute", 7 | description: "Unmutes a user in a voice channel", 8 | options: [ 9 | { 10 | type: "6", 11 | name: "user", 12 | description: "The user to unmute", 13 | required: true 14 | }, 15 | { 16 | type: "3", 17 | name: "reason", 18 | description: "The reason for the unmute", 19 | required: true 20 | } 21 | ], 22 | }); 23 | } 24 | async exec(int, data) { 25 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 26 | 27 | if (!isMod && !int.member.permissions.has("MUTE_MEMBERS") ) { 28 | return int.reply({ 29 | content: "You don't have permission to do that!", 30 | ephemeral: true, 31 | }); 32 | } 33 | 34 | let member = int.options.getMember("user") 35 | let reason = int.options.getString("reason") 36 | 37 | if(member.user.id === int.user.id) { 38 | return int.reply({ 39 | content: "You can't moderate yourself!", 40 | ephemeral: true, 41 | }); 42 | } else if(member.user.id === int.client.user.id) { 43 | return int.reply({ 44 | content: "You can't moderate me!", 45 | ephemeral: true, 46 | }); 47 | } else if(member.user.id === int.guild.ownerId) { 48 | return int.reply({ 49 | content: "You can't moderate the server owner!", 50 | ephemeral: true, 51 | }); 52 | } 53 | 54 | if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't unmute someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | 62 | if(!member.manageable) { 63 | return int.reply({ 64 | content: "I can't unmute that user!", 65 | ephemeral: true, 66 | }); 67 | } 68 | 69 | if(!member.voice.channel) { 70 | return int.reply({ 71 | content: "That user isn't in a voice channel!", 72 | ephemeral: true, 73 | }); 74 | } 75 | 76 | if(!member.voice.selfMute && !member.voice.mute) { 77 | return int.reply({ 78 | content: "That user isn't muted!", 79 | ephemeral: true, 80 | }); 81 | } 82 | 83 | await member.voice.setMute(false, reason).then(async () => { 84 | if(data.modLogs) { 85 | let modChannel = await int.guild.channels.fetch(data.modLogs); 86 | if(modChannel) { 87 | let embed = new MessageEmbed() 88 | .setAuthor({name:`${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, iconURL: `${int.user.avatarURL()}`}) 89 | .setTitle("User unmuted") 90 | .setColor("#2f3136") 91 | .setDescription(`Reason: ${reason}`) 92 | .addFields( 93 | {name: "User", value: `${member}`, inline: true}, 94 | {name: "Moderator", value:`${int.member}`, inline: true}, 95 | ) 96 | .setTimestamp(); 97 | modChannel.send({embeds: [embed]}); 98 | } 99 | } 100 | 101 | return int.reply({ 102 | content: `${member} has been unmuted! Reason: **${reason}**`, 103 | ephemeral: true, 104 | }); 105 | }); 106 | 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /src/commands/warns/unwarn.js: -------------------------------------------------------------------------------- 1 | const warnings = require("../../models/Warns"); 2 | const {MessageEmbed} = require("discord.js"); 3 | 4 | module.exports = class Unwarn extends Interaction { 5 | constructor() { 6 | super({ 7 | name: "unwarn", 8 | description: "Unwarns a user", 9 | options: [ 10 | { 11 | type: "6", 12 | name: "user", 13 | description: "The user to warn", 14 | required: true, 15 | }, 16 | { 17 | type: "3", 18 | name: "warn", 19 | description: "The ID of the warn. Type all to remove all warns", 20 | required: true, 21 | } 22 | ], 23 | }); 24 | } 25 | async exec(int, data) { 26 | 27 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 28 | 29 | if (!isMod && !int.member.permissions.has("MODERATE_MEMBERS")) { 30 | return int.reply({ 31 | content: "You don't have permission to do that!", 32 | ephemeral: true, 33 | }); 34 | } 35 | 36 | const member = int.options.getMember("user"); 37 | let warn = int.options.getString("warn"); 38 | 39 | if(member.user.id === int.user.id) { 40 | return int.reply({ 41 | content: "You can't moderate yourself!", 42 | ephemeral: true, 43 | }); 44 | } else if(member.user.id === int.client.user.id) { 45 | return int.reply({ 46 | content: "You can't moderate me!", 47 | ephemeral: true, 48 | }); 49 | } else if(member.user.id === int.guild.ownerId) { 50 | return int.reply({ 51 | content: "You can't moderate the server owner!", 52 | ephemeral: true, 53 | }); 54 | } else if(int.member.roles.highest.position < member.roles.highest.position) { 55 | return int.reply({ 56 | content: "You can't warn someone with a higher role than you!", 57 | ephemeral: true, 58 | }); 59 | } 60 | 61 | let warning = await warnings.findOne({ 62 | _id: member.user.id, 63 | guildID: int.guild.id, 64 | }); 65 | 66 | if(!warning || warning.warns.length <= 0) { 67 | return int.reply({ 68 | content: "That user doesn't have any warnings!", 69 | ephemeral: true, 70 | }); 71 | } 72 | 73 | if(warn === "all") { 74 | warning.warns = []; 75 | await warning.save(); 76 | 77 | if(data.modLogs) { 78 | let modChannel = await int.guild.channels.fetch(data.modLogs); 79 | if (modChannel) { 80 | let embed = new MessageEmbed() 81 | .setAuthor({ 82 | name: `${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, 83 | iconURL: `${int.user.avatarURL()}` 84 | }) 85 | .setTitle("User unwarned") 86 | .setColor("#2f3136") 87 | .setDescription(`Warn: All`) 88 | .addFields( 89 | {name: "User", value: `${member}`, inline: true}, 90 | {name: "Moderator", value: `${int.member}`, inline: true}, 91 | ) 92 | .setTimestamp(); 93 | modChannel.send({embeds: [embed]}); 94 | } 95 | } 96 | 97 | return int.reply({ 98 | content: `Successfully removed all warns from ${member}!`, 99 | ephemeral: true, 100 | }); 101 | } else { 102 | if(isNaN(parseInt(warn))) { 103 | return int.reply({ 104 | content: "That's not a valid warning ID!", 105 | ephemeral: true, 106 | }); 107 | } 108 | warn = parseInt(warn); 109 | 110 | if(warn <= 0 || warn > warning.warns.length) { 111 | return int.reply({ 112 | content: "Provide a valid warn ID!", 113 | ephemeral: true, 114 | }); 115 | } 116 | 117 | let old = warning.warns.find((w, i) => i === warn - 1); 118 | 119 | 120 | let index = warning.warns.indexOf(old); 121 | 122 | if(data.modLogs) { 123 | let modChannel = await int.guild.channels.fetch(data.modLogs); 124 | if (modChannel) { 125 | let embed = new MessageEmbed() 126 | .setAuthor({ 127 | name: `${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, 128 | iconURL: `${int.user.avatarURL()}` 129 | }) 130 | .setTitle("User unwarned") 131 | .setColor("#2f3136") 132 | .setDescription(`Warn: ${old.reason}`) 133 | .addFields( 134 | {name: "User", value: `${member}`, inline: true}, 135 | {name: "Moderator", value: `${int.member}`, inline: true}, 136 | ) 137 | .setTimestamp(); 138 | modChannel.send({embeds: [embed]}); 139 | } 140 | } 141 | warning.warns.splice(index, 1); 142 | 143 | await warning.save(); 144 | 145 | 146 | return int.reply({ 147 | content: `Successfully removed warn \`${warn}\` from ${member}!`, 148 | ephemeral: true, 149 | }); 150 | } 151 | } 152 | }; 153 | -------------------------------------------------------------------------------- /src/commands/warns/warn.js: -------------------------------------------------------------------------------- 1 | const warnings = require("../../models/Warns"); 2 | const {MessageEmbed} = require("discord.js"); 3 | 4 | module.exports = class Warn extends Interaction { 5 | constructor() { 6 | super({ 7 | name: "warn", 8 | description: "Warns a user", 9 | options: [ 10 | { 11 | type: "6", 12 | name: "user", 13 | description: "The user to warn", 14 | required: true, 15 | }, 16 | { 17 | type: "3", 18 | name: "reason", 19 | description: "The reason for the warning", 20 | required: true, 21 | }, 22 | ], 23 | }); 24 | } 25 | async exec(int, data) { 26 | 27 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 28 | 29 | if (!isMod && !int.member.permissions.has("MODERATE_MEMBERS")) { 30 | return int.reply({ 31 | content: "You don't have permission to do that!", 32 | ephemeral: true, 33 | }); 34 | } 35 | 36 | const member = int.options.getMember("user"); 37 | const reason = int.options.getString("reason"); 38 | 39 | 40 | if(member.user.id === int.user.id) { 41 | return int.reply({ 42 | content: "You can't warn yourself!", 43 | ephemeral: true, 44 | }); 45 | } else if(member.user.id === int.client.user.id) { 46 | return int.reply({ 47 | content: "You can't warn me!", 48 | ephemeral: true, 49 | }); 50 | } else if(member.user.id === int.guild.ownerId) { 51 | return int.reply({ 52 | content: "You can't warn the server owner!", 53 | ephemeral: true, 54 | }); 55 | } else if(int.member.roles.highest.position < member.roles.highest.position) { 56 | return int.reply({ 57 | content: "You can't warn someone with a higher role than you!", 58 | ephemeral: true, 59 | }); 60 | } 61 | 62 | let warning = await warnings.findOne({ 63 | _id: member.user.id, 64 | guildID: int.guild.id, 65 | }); 66 | 67 | if(warning) { 68 | warning.warns.push({ 69 | moderator: int.user.id, 70 | reason: reason, 71 | date: Date.now(), 72 | }); 73 | 74 | await warning.save(); 75 | 76 | } else { 77 | warning = new warnings({ 78 | _id: member.user.id, 79 | guildID: int.guild.id, 80 | warns: [{ 81 | moderator: int.user.id, 82 | reason: reason, 83 | date: Date.now(), 84 | }], 85 | }); 86 | await warning.save(); 87 | } 88 | 89 | if(data.modLogs) { 90 | let modChannel = await int.guild.channels.fetch(data.modLogs); 91 | if (modChannel) { 92 | let embed = new MessageEmbed() 93 | .setAuthor({ 94 | name: `${int.user.username} ${int.member.nickname ? `(${int.member.nickname})` : ""}`, 95 | iconURL: `${int.user.avatarURL()}` 96 | }) 97 | .setTitle("User warned") 98 | .setColor("#2f3136") 99 | .setDescription(`Reason: ${reason}`) 100 | .addFields( 101 | {name: "User", value: `${member}`, inline: true}, 102 | {name: "Moderator", value: `${int.member}`, inline: true}, 103 | ) 104 | .setTimestamp(); 105 | modChannel.send({embeds: [embed]}); 106 | } 107 | } 108 | return int.reply({ 109 | content: `${member} has been warned! Reason: **${reason}**`, 110 | ephemeral: true, 111 | }); 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /src/commands/warns/warns.js: -------------------------------------------------------------------------------- 1 | const warnings = require("../../models/Warns"); 2 | const paginationEmbed = require("../../utils/Pagination"); 3 | const {msToSeconds} = require("../../utils/Utils") 4 | const {MessageEmbed, MessageButton} = require("discord.js") 5 | 6 | module.exports = class Warns extends Interaction { 7 | constructor() { 8 | super({ 9 | name: "warns", 10 | description: "Displays the warns of a user", 11 | options: [ 12 | { 13 | type: "6", 14 | name: "user", 15 | description: "The user to warn", 16 | required: true, 17 | }, 18 | ], 19 | }); 20 | } 21 | async exec(int, data) { 22 | 23 | let isMod = data.modRoles.some((r) => int.member._roles.includes(r)); 24 | 25 | if (!isMod && !int.member.permissions.has("MODERATE_MEMBERS")) { 26 | return int.reply({ 27 | content: "You don't have permission to do that!", 28 | ephemeral: true, 29 | }); 30 | } 31 | 32 | const member = int.options.getMember("user"); 33 | 34 | let warning = await warnings.findOne({ 35 | _id: member.user.id, 36 | guildID: int.guild.id, 37 | }); 38 | 39 | if (!warning || warning.warns.length <= 0) { 40 | return int.reply({ 41 | content: "That user doesn't have any warnings!", 42 | ephemeral: true, 43 | }); 44 | } 45 | 46 | let btn1 = new MessageButton() 47 | .setCustomId("previousbtn") 48 | .setLabel("Previous") 49 | .setStyle("SECONDARY"); 50 | 51 | const btn2 = new MessageButton() 52 | .setCustomId("nextbtn") 53 | .setLabel("Next") 54 | .setStyle("PRIMARY"); 55 | 56 | let currentEmbedItems = []; 57 | let embedItemArray = []; 58 | let pages = []; 59 | 60 | let buttonList = [btn1, btn2]; 61 | 62 | if (warning.warns.length > 5) { 63 | warning.warns.forEach((w, i) => { 64 | w.index = i + 1; 65 | if (currentEmbedItems.length < 5) currentEmbedItems.push(w); 66 | else { 67 | embedItemArray.push(currentEmbedItems); 68 | currentEmbedItems = [w]; 69 | } 70 | 71 | }); 72 | embedItemArray.push(currentEmbedItems); 73 | 74 | embedItemArray.forEach((x) => { 75 | let warns = x 76 | .map((w) => `**ID: ${w.index}**\n**Reason:** ${w.reason}\n**Date:** \n**Mod:** <@${w.moderator}>`) 77 | .join("\n\n"); 78 | let emb = new MessageEmbed() 79 | .setTitle(`${member.user.username}'s warnings`) 80 | .setColor("#2f3136") 81 | .setThumbnail(member.user.avatarURL()) 82 | .setDescription( 83 | `${warns}` 84 | ); 85 | pages.push(emb); 86 | }); 87 | 88 | await paginationEmbed(int, pages, buttonList); 89 | } else { 90 | let warns = warning.warns 91 | .map((w, i) => `**ID: ${i + 1}**\n**Reason:** ${w.reason}\n**Date:** \n**Mod:** <@${w.moderator}>`) 92 | .join("\n\n"); 93 | 94 | let emb = new MessageEmbed() 95 | .setTitle(`${member.user.username}'s warnings`) 96 | .setColor("#2f3136") 97 | .setThumbnail(member.user.avatarURL()) 98 | .setDescription( 99 | `${warns}` 100 | ) 101 | .setFooter("Page 1 / 1"); 102 | 103 | return int.reply({embeds: [emb]}) 104 | } 105 | } 106 | }; 107 | -------------------------------------------------------------------------------- /src/events/client/ready.js: -------------------------------------------------------------------------------- 1 | module.exports = class Ready extends Event { 2 | constructor() { 3 | super({ 4 | name: "ready", 5 | once: false, 6 | }); 7 | } 8 | 9 | async exec() { 10 | this.client.user.setActivity(`bad members`, { type: "WATCHING" }); 11 | 12 | let allMembers = new Set(); 13 | this.client.guilds.cache.forEach((guild) => { 14 | guild.members.cache.forEach((member) => { 15 | allMembers.add(member.user.id); 16 | }); 17 | }); 18 | 19 | let allChannels = new Set(); 20 | this.client.guilds.cache.forEach((guild) => { 21 | guild.channels.cache.forEach((channel) => { 22 | allChannels.add(channel.id); 23 | }); 24 | }); 25 | this.client.logger.log(`Connected into ${this.client.user.tag}`, { 26 | tag: "Ready", 27 | }); 28 | this.client.logger.log( 29 | `Watching ${this.client.guilds.cache.size} servers | ${allMembers.size} members | ${allChannels.size} channels`, 30 | { 31 | tag: "Data", 32 | } 33 | ); 34 | 35 | 36 | for (const guild of this.client.guilds.cache.values()) { 37 | await this.client.loadInteractions(guild.id); 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /src/events/guild/channelCreate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class ChannelCreate extends Event { 3 | constructor() { 4 | super({ 5 | name: "channelCreate", 6 | once: false, 7 | }); 8 | } 9 | async exec(channel) { 10 | if(!channel.guild) return; 11 | if(channel.type === "GUILD_NEWS_THREAD") return; 12 | if(channel.type === "GUILD_PUBLIC_THREAD") return; 13 | if(channel.type === "GUILD_PRIVATE_THREAD ") return; 14 | 15 | const data = await this.client.getGuild({ _id: channel.guild.id }); 16 | 17 | if (data.logsChannel) { 18 | let logsChannel = await channel.guild.channels.fetch(data.logsChannel); 19 | if (logsChannel) { 20 | let emb = new MessageEmbed() 21 | .setColor("#70ec46") 22 | .setTitle("Channel Created") 23 | .setDescription(`**${channel.name}** has been created`) 24 | .addField(`Channel`, `${channel}`, true) 25 | .setTimestamp(); 26 | 27 | logsChannel.send({embeds: [emb]}); 28 | } 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/events/guild/channelDelete.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class ChannelDelete extends Event { 3 | constructor() { 4 | super({ 5 | name: "channelDelete", 6 | once: false, 7 | }); 8 | } 9 | async exec(channel) { 10 | if(!channel.guild) return; 11 | if(channel.type === "GUILD_NEWS_THREAD") return; 12 | if(channel.type === "GUILD_PUBLIC_THREAD") return; 13 | if(channel.type === "GUILD_PRIVATE_THREAD ") return; 14 | 15 | const data = await this.client.getGuild({ _id: channel.guild.id }); 16 | 17 | if (data.logsChannel) { 18 | let logsChannel = await channel.guild.channels.fetch(data.logsChannel); 19 | if (logsChannel) { 20 | let emb = new MessageEmbed() 21 | .setColor("#e15050") 22 | .setTitle("Channel Deleted") 23 | .setDescription(`**${channel.name}** has been deleted`) 24 | .addField(`Channel`, `${channel.name}`, true) 25 | .setTimestamp(); 26 | 27 | logsChannel.send({embeds: [emb]}); 28 | } 29 | } 30 | 31 | if (data.modLogs && data.modLogs === channel.id) { 32 | data.modLogs = undefined; 33 | await data.save(); 34 | } 35 | if (data.logsChannel && data.logsChannel === channel.id) { 36 | data.logsChannel = undefined; 37 | await data.save(); 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /src/events/guild/channelUpdate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class ChannelUpdate extends Event { 3 | constructor() { 4 | super({ 5 | name: "channelUpdate", 6 | once: false, 7 | }); 8 | } 9 | async exec(oldChannel, newChannel) { 10 | if(!oldChannel.guild) return; 11 | if(oldChannel.type === "GUILD_NEWS_THREAD") return; 12 | if(oldChannel.type === "GUILD_PUBLIC_THREAD") return; 13 | if(oldChannel.type === "GUILD_PRIVATE_THREAD ") return; 14 | 15 | const data = await this.client.getGuild({ _id: oldChannel.guild.id }); 16 | 17 | if(data.logsChannel) { 18 | let channel = await oldChannel.guild.channels.fetch(data.logsChannel); 19 | if (channel) { 20 | if (oldChannel.name !== newChannel.name) { 21 | let emb = new MessageEmbed() 22 | .setTitle('Channel updated') 23 | .setColor("#3ccffa") 24 | .setDescription(`${oldChannel} name has been updated`) 25 | .addField("Old name", `${oldChannel.name}`, true) 26 | .addField("New name", `${newChannel.name}`, true) 27 | .setTimestamp() 28 | 29 | channel.send({embeds: [emb]}) 30 | 31 | } 32 | if(oldChannel.topic !== newChannel.topic) { 33 | let emb = new MessageEmbed() 34 | .setTitle('Channel updated') 35 | .setColor("#3ccffa") 36 | .setDescription(`${oldChannel} topic has been updated`) 37 | .addField("Old topic", `${oldChannel.topic ? oldChannel.topic : "None"}`, true) 38 | .addField("New topic", `${newChannel.topic ? newChannel.topic : "None"}`, true) 39 | .setTimestamp() 40 | 41 | channel.send({embeds: [emb]}) 42 | 43 | } 44 | if(oldChannel.position !== newChannel.position) { 45 | let emb = new MessageEmbed() 46 | .setTitle('Channel updated') 47 | .setColor("#3ccffa") 48 | .setDescription(`${oldChannel} position has been updated`) 49 | .addField("Old position", `${oldChannel.position}`, true) 50 | .addField("New position", `${newChannel.position}`, true) 51 | .setTimestamp() 52 | 53 | channel.send({embeds: [emb]}) 54 | 55 | } 56 | if(oldChannel.type !== newChannel.type) { 57 | let emb = new MessageEmbed() 58 | .setTitle('Channel updated') 59 | .setColor("#3ccffa") 60 | .setDescription(`${oldChannel} type has been updated`) 61 | .addField("Old type", `${oldChannel.type}`, true) 62 | .addField("New type", `${newChannel.type}`, true) 63 | .setTimestamp() 64 | 65 | channel.send({embeds: [emb]}) 66 | 67 | } 68 | if(oldChannel.nsfw !== newChannel.nsfw) { 69 | let emb = new MessageEmbed() 70 | .setTitle('Channel updated') 71 | .setColor("#3ccffa") 72 | .setDescription(`${oldChannel} nsfw type has been updated`) 73 | .addField("Old nsfw", `${oldChannel.nsfw}`, true) 74 | .addField("New nsfw", `${newChannel.nsfw}`, true) 75 | .setTimestamp() 76 | 77 | channel.send({embeds: [emb]}) 78 | 79 | } 80 | if(oldChannel.bitrate !== newChannel.bitrate) { 81 | let emb = new MessageEmbed() 82 | .setTitle('Channel updated') 83 | .setColor("#3ccffa") 84 | .setDescription(`${oldChannel} bitrate has been updated`) 85 | .addField("Old bitrate", `${oldChannel.bitrate}`, true) 86 | .addField("New bitrate", `${newChannel.bitrate}`, true) 87 | .setTimestamp() 88 | 89 | channel.send({embeds: [emb]}) 90 | 91 | } 92 | if(oldChannel.userLimit !== newChannel.userLimit) { 93 | let emb = new MessageEmbed() 94 | .setTitle('Channel updated') 95 | .setColor("#3ccffa") 96 | .setDescription(`${oldChannel} user limit has been updated`) 97 | .addField("Old limit", `${oldChannel.userLimit}`, true) 98 | .addField("New limit", `${newChannel.userLimit}`, true) 99 | .setTimestamp() 100 | 101 | channel.send({embeds: [emb]}) 102 | 103 | } 104 | if(oldChannel.rateLimitPerUser !== newChannel.rateLimitPerUser) { 105 | let emb = new MessageEmbed() 106 | .setTitle('Channel updated') 107 | .setColor("#3ccffa") 108 | .setDescription(`${oldChannel} rate limit has been updated`) 109 | .addField("Old rate limit", `${oldChannel.rateLimitPerUser ? oldChannel.rateLimitPerUser : "None"}`, true) 110 | .addField("New rate limit", `${newChannel.rateLimitPerUser ? newChannel.rateLimitPerUser : "None"}`, true) 111 | .setTimestamp() 112 | 113 | channel.send({embeds: [emb]}) 114 | 115 | } 116 | if(oldChannel.parent !== newChannel.parent) { 117 | let emb = new MessageEmbed() 118 | .setTitle('Channel updated') 119 | .setColor("#3ccffa") 120 | .setDescription(`${oldChannel} parent has been updated`) 121 | .addField("Old parent", `${oldChannel.parent}`, true) 122 | .addField("New parent", `${newChannel.parent}`, true) 123 | .setTimestamp() 124 | 125 | channel.send({embeds: [emb]}) 126 | } 127 | } 128 | } 129 | } 130 | }; 131 | -------------------------------------------------------------------------------- /src/events/guild/guildBanAdd.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | 3 | module.exports = class GuildBanAdd extends Event { 4 | constructor() { 5 | super({ 6 | name: "guildBanAdd", 7 | once: false, 8 | }); 9 | } 10 | async exec(ban) { 11 | const data = await this.client.getGuild({ _id: ban.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await ban.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#e15050") 18 | .setTitle("User Banned") 19 | .setDescription(`**${ban.user.tag}** has been banned`) 20 | .addField(`User`, `${ban.user}`, true) 21 | .addField(`Reason`, `${ban.reason ? `${ban.reason}` : "No reason provided"}`, true) 22 | .setTimestamp(); 23 | 24 | channel.send({embeds: [emb]}); 25 | } 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/events/guild/guildBanRemove.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | 3 | module.exports = class GuildBanRemove extends Event { 4 | constructor() { 5 | super({ 6 | name: "guildBanRemove", 7 | once: false, 8 | }); 9 | } 10 | async exec(ban) { 11 | const data = await this.client.getGuild({ _id: ban.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await ban.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#70ec46") 18 | .setTitle("User Unbanned") 19 | .setDescription(`**${ban.user.tag}** has been unbanned`) 20 | .addField(`User`, `${ban.user}`, true) 21 | .addField(`Reason`, `${ban.reason ? ban.reason : "No reason provided"}`, true) 22 | .setTimestamp(); 23 | 24 | channel.send({embeds: [emb]}); 25 | } 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/events/guild/guildCreate.js: -------------------------------------------------------------------------------- 1 | module.exports = class GuildCreate extends Event { 2 | constructor() { 3 | super({ 4 | name: "guildCreate", 5 | once: false, 6 | }); 7 | } 8 | async exec(guild) { 9 | await this.client.getGuild({ _id: guild.id }); 10 | await this.client.loadInteractions(guild.id); 11 | 12 | this.client.logger.log(`${guild.name} (${guild.id}) just added me!`, { 13 | tag: "guildCreate", 14 | }); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/events/guild/guildDelete.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = class GuildDelete extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildDelete", 6 | once: false, 7 | }); 8 | } 9 | async exec(guild) { 10 | const data = await this.client.getGuild({ _id: guild.id }); 11 | 12 | await data 13 | .delete() 14 | .catch((err) => 15 | this.client.logger.error( 16 | `An error occurred when trying to trigger guildDelete event.\n${ 17 | err.stack ? err + "\n\n" + err.stack : err 18 | }`, 19 | { tag: "guildDelete" } 20 | ) 21 | ); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/events/guild/guildEventCreate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildScheduledEventCreate extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildScheduledEventCreate", 6 | once: false, 7 | }); 8 | } 9 | async exec(guildScheduledEvent) { 10 | 11 | const data = await this.client.getGuild({ _id: guildScheduledEvent.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let logsChannel = await guildScheduledEvent.guild.channels.fetch(data.logsChannel); 15 | if (logsChannel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#70ec46") 18 | .setTitle("Event Created") 19 | .setDescription(`**${guildScheduledEvent.name}** has been created: ${guildScheduledEvent.description}`) 20 | .addField("Event Type", `${guildScheduledEvent.type}`, true) 21 | .addField("Created by", `${guildScheduledEvent.creator}`, true) 22 | .addField("Start Date", `${guildScheduledEvent.scheduledStartAt}`, true) 23 | .addField("End Date", `${guildScheduledEvent.scheduledEndAt ? guildScheduledEvent.scheduledEndAt : "None"}`, true) 24 | .setTimestamp(); 25 | 26 | if(guildScheduledEvent.channel) { 27 | emb.addField("Channel", `${guildScheduledEvent.channel}`, true); 28 | } 29 | 30 | logsChannel.send({embeds: [emb]}); 31 | } 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/events/guild/guildEventDelete.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildScheduledEventDelete extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildScheduledEventDelete", 6 | once: false, 7 | }); 8 | } 9 | async exec(guildScheduledEvent) { 10 | 11 | const data = await this.client.getGuild({ _id: guildScheduledEvent.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let logsChannel = await guildScheduledEvent.guild.channels.fetch(data.logsChannel); 15 | if (logsChannel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#ea4e4e") 18 | .setTitle("Event Deleted") 19 | .setDescription(`**${guildScheduledEvent.name}** has been deleted: ${guildScheduledEvent.description}`) 20 | .addField("Event Type", `${guildScheduledEvent.type}`, true) 21 | .addField("Created by", `${guildScheduledEvent.creator}`, true) 22 | .addField("Start Date", `${guildScheduledEvent.scheduledStartAt}`, true) 23 | .addField("End Date", `${guildScheduledEvent.scheduledEndAt}`, true) 24 | .setTimestamp(); 25 | 26 | if(guildScheduledEvent.channel) { 27 | emb.addField("Channel", `${guildScheduledEvent.channel}`, true); 28 | } 29 | logsChannel.send({embeds: [emb]}); 30 | } 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/events/guild/guildEventUpdate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildScheduledEventUpdate extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildScheduledEventUpdate", 6 | once: false, 7 | }); 8 | } 9 | async exec(oldEvent, newEvent) { 10 | 11 | const data = await this.client.getGuild({ _id: oldEvent.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await oldEvent.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | if(oldEvent.name !== newEvent.name) { 17 | let emb = new MessageEmbed() 18 | .setColor("#3ccffa") 19 | .setTitle("Event Updated") 20 | .setDescription(`[${newEvent.name}](${newEvent}) Event name has been updated`) 21 | .addField("Old name", oldEvent.name, true) 22 | .addField("New name", newEvent.name, true) 23 | .setTimestamp() 24 | 25 | channel.send({embeds: [emb]}); 26 | } 27 | if(oldEvent.channel !== newEvent.channel) { 28 | let emb = new MessageEmbed() 29 | .setColor("#3ccffa") 30 | .setTitle("Event Updated") 31 | .setDescription(`[${newEvent.name}](${newEvent}) Event channel has been updated`) 32 | .addField("Old channel", `${oldEvent.channel ? oldEvent.channel : "None"}`, true) 33 | .addField("New channel",`${newEvent.channel ? newEvent.channel : "None"}`, true) 34 | .setTimestamp() 35 | 36 | channel.send({embeds: [emb]}); 37 | } 38 | if(oldEvent.scheduledStartAt !== newEvent.scheduledStartAt) { 39 | let emb = new MessageEmbed() 40 | .setColor("#3ccffa") 41 | .setTitle("Event Updated") 42 | .setDescription(`[${newEvent.name}](${newEvent}) Event start date has been updated`) 43 | .addField("Old date",`${oldEvent.scheduledStartAt ? oldEvent.scheduledStartAt : "None"}`, true) 44 | .addField("New date", `${newEvent.scheduledStartAt ? newEvent.scheduledStartAt : "None"}`, true) 45 | .setTimestamp() 46 | 47 | channel.send({embeds: [emb]}); 48 | } 49 | if(oldEvent.scheduledEndAt !== newEvent.scheduledEndAt) { 50 | let emb = new MessageEmbed() 51 | .setColor("#3ccffa") 52 | .setTitle("Event Updated") 53 | .setDescription(`[${newEvent.name}](${newEvent}) Event end date has been updated`) 54 | .addField("Old date", `${oldEvent.scheduledEndAt ? oldEvent.scheduledEndAt : "None"}`, true) 55 | .addField("New date", `${newEvent.scheduledEndAt ? newEvent.scheduledEndAt : "None"}`, true) 56 | .setTimestamp() 57 | 58 | channel.send({embeds: [emb]}); 59 | } 60 | if(oldEvent.description !== newEvent.description) { 61 | let emb = new MessageEmbed() 62 | .setColor("#3ccffa") 63 | .setTitle("Event Updated") 64 | .setDescription(`[${newEvent.name}](${newEvent}) Event description has been updated`) 65 | .addField("Old description", `${oldEvent.description ? oldEvent.description : "None"}`, true) 66 | .addField("New description", `${newEvent.description ? newEvent.description : "None"}`, true) 67 | .setTimestamp() 68 | 69 | channel.send({embeds: [emb]}); 70 | } 71 | if(oldEvent.status !== newEvent.status) { 72 | let emb = new MessageEmbed() 73 | .setColor("#3ccffa") 74 | .setTitle("Event Updated") 75 | .setDescription(`[${newEvent.name}](${newEvent}) Event status has been updated`) 76 | .addField("Old status", `${oldEvent.status}`, true) 77 | .addField("New status", `${newEvent.status}`, true) 78 | .setTimestamp() 79 | 80 | channel.send({embeds: [emb]}); 81 | } 82 | if(oldEvent.entityType !== newEvent.entityType) { 83 | let emb = new MessageEmbed() 84 | .setColor("#3ccffa") 85 | .setTitle("Event Updated") 86 | .setDescription(`[${newEvent.name}](${newEvent}) Event type has been updated`) 87 | .addField("Old type", `${oldEvent.entityType}`, true) 88 | .addField("New type", `${newEvent.entityType}`, true) 89 | .setTimestamp() 90 | 91 | channel.send({embeds: [emb]}); 92 | } 93 | if(oldEvent.entityMetadata !== newEvent.entityMetadata) { 94 | let emb = new MessageEmbed() 95 | .setColor("#3ccffa") 96 | .setTitle("Event Updated") 97 | .setDescription(`[${newEvent.name}](${newEvent}) Event location has been updated`) 98 | .addField("Old location", `${oldEvent.entityMetadata?.location ? oldEvent.entityMetadata.location : "None"}`, true) 99 | .addField("New location", `${newEvent.entityMetadata?.location ? newEvent.entityMetadata.location : "None"}`, true) 100 | .setTimestamp() 101 | 102 | channel.send({embeds: [emb]}); 103 | } 104 | if(oldEvent.privacyLevel !== newEvent.privacyLevel) { 105 | let emb = new MessageEmbed() 106 | .setColor("#3ccffa") 107 | .setTitle("Event Updated") 108 | .setDescription(`[${newEvent.name}](${newEvent}) Event privacy level has been updated`) 109 | .addField("Old level", `${oldEvent.privacyLevel}`, true) 110 | .addField("New level", `${newEvent.privacyLevel}`, true) 111 | .setTimestamp() 112 | 113 | channel.send({embeds: [emb]}); 114 | } 115 | } 116 | } 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /src/events/guild/guildEventUserAdd.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildScheduledEventUserAdd extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildScheduledEventUserAdd", 6 | once: false, 7 | }); 8 | } 9 | async exec(guildScheduledEvent, user) { 10 | 11 | const data = await this.client.getGuild({ _id: guildScheduledEvent.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await guildScheduledEvent.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#70ec46") 18 | .setTitle("Event user added") 19 | .setDescription(`**${guildScheduledEvent.name}** Event user has been added`) 20 | .addField("Event", `${guildScheduledEvent.name}`, true) 21 | .addField("User", `${user}`, true) 22 | .setTimestamp(); 23 | 24 | channel.send({embeds: [emb]}); 25 | } 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/events/guild/guildEventUserRemove.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildScheduledEventUserRemove extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildScheduledEventUserRemove", 6 | once: false, 7 | }); 8 | } 9 | async exec(guildScheduledEvent, user) { 10 | 11 | const data = await this.client.getGuild({ _id: guildScheduledEvent.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await guildScheduledEvent.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#ea4e4e") 18 | .setTitle("Event user removed") 19 | .setDescription(`**${guildScheduledEvent.name}** Event user has been removed`) 20 | .addField("Event", `${guildScheduledEvent.name}`, true) 21 | .addField("User", `${user}`, true) 22 | .setTimestamp(); 23 | 24 | channel.send({embeds: [emb]}); 25 | } 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/events/guild/guildMemberAdd.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildMemberAdd extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildMemberAdd", 6 | once: false, 7 | }); 8 | } 9 | async exec(member) { 10 | 11 | const data = await this.client.getGuild({ _id: member.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await member.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#70ec46") 18 | .setAuthor({name: member.user.username, iconURL: member.user.avatarURL()}) 19 | .setTitle("User Joined") 20 | .setDescription(`${member} joined the server`) 21 | .addField("User", `${member}`, true) 22 | .addField("Created at", `${member.user.createdAt}`, true) 23 | .setTimestamp(); 24 | 25 | channel.send({embeds: [emb]}); 26 | } 27 | } 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/events/guild/guildMemberRemove.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class GuildMemberRemove extends Event { 3 | constructor() { 4 | super({ 5 | name: "guildMemberRemove", 6 | once: false, 7 | }); 8 | } 9 | async exec(member) { 10 | 11 | const data = await this.client.getGuild({ _id: member.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await member.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#ea4e4e") 18 | .setAuthor({name: member.user.username, iconURL: member.user.avatarURL()}) 19 | .setTitle("User left") 20 | .setDescription(`${member} left the server`) 21 | .addField("User", `${member}`, true) 22 | .addField("Created at", `${member.user.createdAt}`, true) 23 | .setTimestamp(); 24 | 25 | channel.send({embeds: [emb]}); 26 | } 27 | } 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/events/guild/guildMemberUpdate.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | const {msToSeconds} = require("../../utils/Utils"); 3 | 4 | module.exports = class GuildMemberUpdate extends Event { 5 | constructor() { 6 | super({ 7 | name: "guildMemberUpdate", 8 | once: false, 9 | }); 10 | } 11 | async exec(oldMember, newMember) { 12 | 13 | const data = await this.client.getGuild({ _id: newMember.guild.id }); 14 | 15 | let oldTimeOut = oldMember.communicationDisabledUntilTimestamp; 16 | let newTimeOut = newMember.communicationDisabledUntilTimestamp; 17 | 18 | if (data.logsChannel) { 19 | let channel = await oldMember.guild.channels.fetch(data.logsChannel); 20 | if (channel) { 21 | if (oldTimeOut !== newTimeOut && newTimeOut != null && newTimeOut > Date.now()) { 22 | 23 | let emb = new MessageEmbed() 24 | .setColor("#ea4e4e") 25 | .setAuthor({name:`${newMember.user.username} ${newMember.nickname ? `(${newMember.nickname})` : ""}`, iconURL: `${newMember.user.avatarURL()}`}) 26 | .setTitle("User Timed Out") 27 | .setDescription(`${newMember} has been timed out`) 28 | .addField("Until", ``) 29 | .setTimestamp(); 30 | 31 | channel.send({embeds: [emb]}); 32 | } 33 | if(oldTimeOut !== newTimeOut && newTimeOut == null) { 34 | 35 | let emb = new MessageEmbed() 36 | .setColor("#70ec46") 37 | .setAuthor({name:`${newMember.user.username} ${newMember.nickname ? `(${newMember.nickname})` : ""}`, iconURL: `${newMember.user.avatarURL()}`}) 38 | .setTitle("User Time Out ended") 39 | .setDescription(`${newMember} time out has ended`) 40 | .setTimestamp(); 41 | 42 | channel.send({embeds: [emb]}); 43 | } 44 | if(oldMember.nickname !== newMember.nickname) { 45 | let emb = new MessageEmbed() 46 | .setColor("#3ccffa") 47 | .setTitle("User updated") 48 | .setDescription(`${newMember} nickname has been updated`) 49 | .addField("Old nickname", `${oldMember.nickname ? oldMember.nickname : "None"}`) 50 | .addField("New nickname", `${newMember.nickname ? newMember.nickname : "None"}`) 51 | .setTimestamp(); 52 | 53 | channel.send({embeds: [emb]}); 54 | } 55 | if(oldMember.avatar !== newMember.avatar) { 56 | let emb = new MessageEmbed() 57 | .setColor("#3ccffa") 58 | .setTitle("User updated") 59 | .setDescription(`${newMember} server avatar has been updated`) 60 | .addField("Old avatar", `[Click here](${oldMember.avatarURL()})`) 61 | .addField("New avatar", `[Click here](${newMember.avatarURL()})`) 62 | .setTimestamp(); 63 | 64 | channel.send({embeds: [emb]}); 65 | } 66 | if(oldMember.user.username !== newMember.user.username) { 67 | let emb = new MessageEmbed() 68 | .setColor("#3ccffa") 69 | .setTitle("User updated") 70 | .setDescription(`${newMember} username has been updated`) 71 | .addField("Old username", `${oldMember.user.username}`) 72 | .addField("New username", `${newMember.user.username}`) 73 | .setTimestamp(); 74 | 75 | channel.send({embeds: [emb]}); 76 | } 77 | if(oldMember.roles.cache.size !== newMember.roles.cache.size) { 78 | let difference; 79 | if(oldMember.roles.cache.size > newMember.roles.cache.size) { 80 | 81 | difference = oldMember.roles.cache.filter(r => !newMember.roles.cache.has(r.id)); 82 | 83 | let emb = new MessageEmbed() 84 | .setColor("#3ccffa") 85 | .setTitle("User updated") 86 | .setDescription(`${newMember} role has been removed`) 87 | .addField("Role", `${difference.map(r => r).join(" ")}`) 88 | .setTimestamp(); 89 | 90 | channel.send({embeds: [emb]}); 91 | } else { 92 | difference = newMember.roles.cache.filter(r => !oldMember.roles.cache.has(r.id)); 93 | 94 | let emb = new MessageEmbed() 95 | .setColor("#3ccffa") 96 | .setTitle("User updated") 97 | .setDescription(`${newMember} role has been added`) 98 | .addField("Role", `${difference.map(r => r).join(" ")}`) 99 | .setTimestamp(); 100 | 101 | channel.send({embeds: [emb]}); 102 | } 103 | 104 | 105 | } 106 | } 107 | } 108 | } 109 | }; 110 | -------------------------------------------------------------------------------- /src/events/guild/roleCreate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class RoleCreate extends Event { 3 | constructor() { 4 | super({ 5 | name: "roleCreate", 6 | once: false, 7 | }); 8 | } 9 | async exec(role) { 10 | 11 | const data = await this.client.getGuild({ _id: role.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await role.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | let emb = new MessageEmbed() 17 | .setColor("#70ec46") 18 | .setTitle("Role created") 19 | .setDescription(`${role} created`) 20 | .addField("Role", `${role}`, true) 21 | .setTimestamp(); 22 | 23 | channel.send({embeds: [emb]}); 24 | } 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/events/guild/roleDelete.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | 3 | module.exports = class RoleDelete extends Event { 4 | constructor() { 5 | super({ 6 | name: "roleDelete", 7 | once: false, 8 | }); 9 | } 10 | async exec(role) { 11 | const data = await this.client.getGuild({ _id: role.guild.id }); 12 | 13 | let mod = data.modRoles.find((r) => r === role.id); 14 | if (mod) { 15 | let index = data.modRoles.indexOf(role.id); 16 | data.modRoles.splice(index, 1); 17 | await data.save(); 18 | } 19 | 20 | if (data.logsChannel) { 21 | let channel = await role.guild.channels.fetch(data.logsChannel); 22 | if (channel) { 23 | let emb = new MessageEmbed() 24 | .setColor("#ea4e4e") 25 | .setTitle("Role deleted") 26 | .setDescription(`**${role.name}** deleted`) 27 | .addField("Role", `${role.name}`, true) 28 | .addField("Created at", `${role.createdAt}`, true) 29 | .setTimestamp(); 30 | 31 | channel.send({embeds: [emb]}); 32 | } 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /src/events/guild/roleUpdate.js: -------------------------------------------------------------------------------- 1 | const { MessageEmbed } = require("discord.js"); 2 | module.exports = class RoleUpdate extends Event { 3 | constructor() { 4 | super({ 5 | name: "roleUpdate", 6 | once: false, 7 | }); 8 | } 9 | async exec(oldRole, newRole) { 10 | 11 | const data = await this.client.getGuild({ _id: newRole.guild.id }); 12 | 13 | if (data.logsChannel) { 14 | let channel = await newRole.guild.channels.fetch(data.logsChannel); 15 | if (channel) { 16 | if(oldRole.name !== newRole.name) { 17 | let emb = new MessageEmbed() 18 | .setColor("#3ccffa") 19 | .setTitle("Role updated") 20 | .setDescription(`${newRole} name has been updated`) 21 | .addField("Old name", `${oldRole.name}`) 22 | .addField("New name", `${newRole.name}`) 23 | .setTimestamp(); 24 | 25 | channel.send({embeds: [emb]}); 26 | } 27 | if(oldRole.hexColor !== newRole.hexColor) { 28 | let emb = new MessageEmbed() 29 | .setColor("#3ccffa") 30 | .setTitle("Role updated") 31 | .setDescription(`${newRole} color has been updated`) 32 | .addField("Old color", `${oldRole.hexColor}`) 33 | .addField("New color", `${newRole.hexColor}`) 34 | .setTimestamp(); 35 | 36 | channel.send({embeds: [emb]}); 37 | } 38 | if(oldRole.hoist !== newRole.hoist) { 39 | let emb = new MessageEmbed() 40 | .setColor("#3ccffa") 41 | .setTitle("Role updated") 42 | .setDescription(`${newRole} hoist has been updated`) 43 | .addField("Old hoist", `${oldRole.hoist}`) 44 | .addField("New hoist", `${newRole.hoist}`) 45 | .setTimestamp(); 46 | 47 | channel.send({embeds: [emb]}); 48 | } 49 | if(oldRole.icon !== newRole.icon) { 50 | let emb = new MessageEmbed() 51 | .setColor("#3ccffa") 52 | .setTitle("Role updated") 53 | .setDescription(`${newRole} icon has been updated`) 54 | .addField("Old icon", `${oldRole.icon ? oldRole.iconURL : "None"}`) 55 | .addField("New icon", `${newRole.icon ? newRole.iconURL : "None"}`) 56 | .setTimestamp(); 57 | 58 | channel.send({embeds: [emb]}); 59 | } 60 | if(oldRole.mentionable !== newRole.mentionable) { 61 | let emb = new MessageEmbed() 62 | .setColor("#3ccffa") 63 | .setTitle("Role updated") 64 | .setDescription(`${newRole} mentionable has been updated`) 65 | .addField("Old mentionable", `${oldRole.mentionable}`) 66 | .addField("New mentionable", `${newRole.mentionable}`) 67 | .setTimestamp(); 68 | 69 | channel.send({embeds: [emb]}); 70 | } 71 | if(oldRole.position !== newRole.position) { 72 | let emb = new MessageEmbed() 73 | .setColor("#3ccffa") 74 | .setTitle("Role updated") 75 | .setDescription(`${newRole} position has been updated`) 76 | .addField("Old position", `${oldRole.position}`) 77 | .addField("New position", `${newRole.position}`) 78 | .setTimestamp(); 79 | 80 | channel.send({embeds: [emb]}); 81 | } 82 | } 83 | } 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /src/events/guild/threadCreate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class ThreadCreate extends Event { 3 | constructor() { 4 | super({ 5 | name: "threadCreate", 6 | once: false, 7 | }); 8 | } 9 | async exec(thread) { 10 | 11 | const data = await this.client.getGuild({ _id: thread.guild.id }); 12 | 13 | 14 | if(thread.type !== "GUILD_NEWS_THREAD" && thread.type !== "GUILD_PUBLIC_THREAD" && thread.type !== "GUILD_PRIVATE_THREAD ") return; 15 | 16 | if (data.logsChannel) { 17 | let channel = await thread.guild.channels.fetch(data.logsChannel); 18 | if (channel) { 19 | let emb = new MessageEmbed() 20 | .setColor("#70ec46") 21 | .setTitle("Thread created") 22 | .setDescription(`${thread} has been created`) 23 | .addField(`Thread`, `${thread}`, true) 24 | .addField("Created by", `<@${thread.ownerId}>`, true) 25 | .setTimestamp(); 26 | 27 | channel.send({embeds: [emb]}); 28 | } 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/events/guild/threadDelete.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class ThreadDelete extends Event { 3 | constructor() { 4 | super({ 5 | name: "threadDelete", 6 | once: false, 7 | }); 8 | } 9 | async exec(thread) { 10 | 11 | if(thread.type !== "GUILD_NEWS_THREAD" && thread.type !== "GUILD_PUBLIC_THREAD" && thread.type !== "GUILD_PRIVATE_THREAD ") return; 12 | 13 | const data = await this.client.getGuild({ _id: thread.guild.id }); 14 | 15 | if (data.logsChannel) { 16 | let channel = await thread.guild.channels.fetch(data.logsChannel); 17 | if (channel) { 18 | let emb = new MessageEmbed() 19 | .setColor("#ea4e4e") 20 | .setTitle("Thread deleted") 21 | .setDescription(`**${thread.name}** has been deleted`) 22 | .addField(`Thread`, `${thread.name}`, true) 23 | .setTimestamp(); 24 | 25 | channel.send({embeds: [emb]}); 26 | } 27 | } 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/events/guild/threadUpdate.js: -------------------------------------------------------------------------------- 1 | const {MessageEmbed} = require("discord.js"); 2 | module.exports = class ThreadUpdate extends Event { 3 | constructor() { 4 | super({ 5 | name: "threadUpdate", 6 | once: false, 7 | }); 8 | } 9 | async exec(oldThread, newThread) { 10 | 11 | if(oldThread.type !== "GUILD_NEWS_THREAD" && oldThread.type !== "GUILD_PUBLIC_THREAD" && oldThread.type !== "GUILD_PRIVATE_THREAD") return; 12 | 13 | const data = await this.client.getGuild({ _id: oldThread.guild.id }); 14 | 15 | if(data.logsChannel) { 16 | let channel = await oldThread.guild.channels.fetch(data.logsChannel); 17 | if (channel) { 18 | if (oldThread.name !== newThread.name) { 19 | let emb = new MessageEmbed() 20 | .setTitle("Thread updated") 21 | .setColor("#3ccffa") 22 | .setDescription(`${oldThread} name has been updated`) 23 | .addField("Old name", `${oldThread.name}`, true) 24 | .addField("New name", `${newThread.name}`, true) 25 | .setTimestamp() 26 | 27 | channel.send({embeds: [emb]}) 28 | } 29 | if(oldThread.type !== newThread.type) { 30 | let emb = new MessageEmbed() 31 | .setTitle("Thread updated") 32 | .setColor("#3ccffa") 33 | .setDescription(`${oldThread} type has been updated`) 34 | .addField("Old type", `${oldThread.type}`, true) 35 | .addField("New type", `${newThread.type}`, true) 36 | .setTimestamp() 37 | 38 | channel.send({embeds: [emb]}) 39 | 40 | } 41 | if(oldThread.rateLimitPerUser !== newThread.rateLimitPerUser) { 42 | let emb = new MessageEmbed() 43 | .setTitle("Thread updated") 44 | .setColor("#3ccffa") 45 | .setDescription(`${oldThread} rate limit has been updated`) 46 | .addField("Old rate limit", `${oldThread.rateLimitPerUser}`, true) 47 | .addField("New rate limit", `${newThread.rateLimitPerUser}`, true) 48 | .setTimestamp() 49 | 50 | channel.send({embeds: [emb]}) 51 | 52 | } 53 | if(oldThread.parent !== newThread.parent) { 54 | let emb = new MessageEmbed() 55 | .setTitle("Thread updated") 56 | .setColor("#3ccffa") 57 | .setDescription(`${oldThread} parent has been updated`) 58 | .addField("Old parent", `${oldThread.parent}`, true) 59 | .addField("New parent", `${newThread.parent}`, true) 60 | .setTimestamp() 61 | 62 | channel.send({embeds: [emb]}) 63 | } 64 | if(oldThread.archived !== newThread.archived) { 65 | let emb = new MessageEmbed() 66 | .setTitle("Thread updated") 67 | .setColor("#3ccffa") 68 | .setDescription(`${oldThread} archived has been updated`) 69 | .addField("Old archived", `${oldThread.archived}`, true) 70 | .addField("New archived", `${newThread.archived}`, true) 71 | .setTimestamp() 72 | 73 | channel.send({embeds: [emb]}) 74 | } 75 | } 76 | } 77 | } 78 | }; 79 | -------------------------------------------------------------------------------- /src/events/interactions/interactionCreate.js: -------------------------------------------------------------------------------- 1 | module.exports = class InteractionCreate extends Event { 2 | constructor() { 3 | super({ 4 | name: "interactionCreate", 5 | once: false, 6 | }); 7 | } 8 | async exec(interaction) { 9 | if (interaction.guild) { 10 | const data = await this.client.getGuild({ 11 | _id: interaction.guildId, 12 | }); 13 | /* Slash commands */ 14 | if (interaction.isCommand()) 15 | return this.client.emit("slashCommands", interaction, data); 16 | 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/events/interactions/slashCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = class SlashCommands extends Event { 2 | constructor() { 3 | super({ 4 | name: "slashCommands", 5 | once: false, 6 | }); 7 | } 8 | 9 | async exec(interaction, data) { 10 | const cmd = this.client.interactions.get(interaction.commandName); 11 | 12 | if (!cmd) return; 13 | 14 | try { 15 | await cmd.exec(interaction, data); 16 | } catch (err) { 17 | if (interaction.replied) { 18 | if (!interaction.ephemeral) { 19 | await interaction.editReply({ 20 | content: 21 | "Oops! It seems that my devs spilled coffee on the computer :/", 22 | }); 23 | } else { 24 | interaction.channel.send({ 25 | content: 26 | "Oops! It seems that my devs spilled coffee on the computer :/", 27 | }); 28 | } 29 | } else { 30 | interaction.reply({ 31 | ephemeral: true, 32 | content: 33 | "Oops! It seems that my devs spilled coffee on the computer :/", 34 | }); 35 | } 36 | return this.client.logger.error( 37 | `An error occured while trying to trigger slashCommands\n${ 38 | err.stack ? err + "\n\n" + err.stack : err 39 | }`, 40 | { 41 | tag: "Interaction", 42 | } 43 | ); 44 | } 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /src/models/Guilds.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | module.exports = model( 4 | "Guilds", 5 | new Schema( 6 | { 7 | _id: {type: String}, 8 | modRoles: {type: Array, default: null}, 9 | logsChannel: {type: String, default: null}, 10 | modLogs: {type: String, default: null}, 11 | }, 12 | { versionKey: false } 13 | ) 14 | ); 15 | -------------------------------------------------------------------------------- /src/models/Warns.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require("mongoose"); 2 | 3 | module.exports = model( 4 | "Warns", 5 | new Schema( 6 | { 7 | _id: {type: String}, 8 | guildID: {type: Array, default: null}, 9 | warns: {type: Array, default: null}, 10 | }, 11 | { versionKey: false } 12 | ) 13 | ); 14 | -------------------------------------------------------------------------------- /src/struct/Bot.js: -------------------------------------------------------------------------------- 1 | const {Client, Collection, Intents} = require("discord.js"); 2 | const {connect, connection: db} = require("mongoose"); 3 | const {Routes} = require("discord-api-types/v9"); 4 | const {REST} = require("@discordjs/rest"); 5 | const {resolve} = require("path"); 6 | const {sync} = require("glob"); 7 | 8 | require("./Interaction"); 9 | require("./Event"); 10 | 11 | module.exports = class Bot extends Client { 12 | constructor() { 13 | super({ 14 | intents: Object.values(Intents.FLAGS), 15 | partials: ["MESSAGE", "REACTION"], 16 | allowedMentions: { 17 | parse: ["roles", "users"], 18 | repliedUser: false, 19 | }, 20 | }); 21 | 22 | this.events = new Collection(); 23 | this.logger = require("../utils/Logger"); 24 | this.interactions = new Collection(); 25 | 26 | this.database = {}; 27 | this.guildsData = require("../models/Guilds"); 28 | this.database.guilds = new Collection(); 29 | 30 | db.on("connected", async () => 31 | this.logger.log( 32 | `Successfully connected to the database! (Latency: ${Math.round(await this.databasePing())}ms)`, 33 | {tag: "Database"} 34 | ) 35 | ); 36 | db.on("disconnected", () => 37 | this.logger.error("Disconnected from the database!", {tag: "Database"}) 38 | ); 39 | db.on("error", (error) => 40 | this.logger.error( 41 | `Unable to connect to the database!\n${ 42 | error.stack ? error + "\n\n" + error.stack : error 43 | }`, 44 | { 45 | tag: "Database", 46 | } 47 | ) 48 | ); 49 | db.on("reconnected", async () => 50 | this.logger.log( 51 | `Reconnected to the database! (Latency: ${Math.round( 52 | await this.databasePing() 53 | )}ms)`, 54 | {tag: "Database"} 55 | ) 56 | ); 57 | } 58 | 59 | async getGuild({_id: guildId}, check) { 60 | if (this.database.guilds.get(guildId)) { 61 | return check 62 | ? this.database.guilds.get(guildId).toJSON() 63 | : this.database.guilds.get(guildId); 64 | } else { 65 | let guildData = check 66 | ? await this.guildsData.findOne({_id: guildId}).lean() 67 | : await this.guildsData.findOne({_id: guildId}); 68 | if (guildData) { 69 | if (!check) this.database.guilds.set(guildId, guildData); 70 | return guildData; 71 | } else { 72 | guildData = new this.guildsData({_id: guildId}); 73 | await guildData.save(); 74 | this.database.guilds.set(guildId, guildData); 75 | return check ? guildData.toJSON() : guildData; 76 | } 77 | } 78 | } 79 | 80 | /* Start database */ 81 | async loadDatabase() { 82 | return connect(process.env.MONGO, { 83 | useNewUrlParser: true, 84 | useUnifiedTopology: true, 85 | }); 86 | } 87 | 88 | /* Get database ping */ 89 | async databasePing() { 90 | const cNano = process.hrtime(); 91 | await db.db.command({ping: 1}); 92 | const time = process.hrtime(cNano); 93 | return (time[0] * 1e9 + time[1]) * 1e-6; 94 | } 95 | 96 | 97 | /* Load slash commands for each guilds */ 98 | async loadInteractions(guildId) { 99 | const intFile = await sync(resolve("./src/commands/**/*.js")); 100 | intFile.forEach((filepath) => { 101 | const File = require(filepath); 102 | if (!(File.prototype instanceof Interaction)) return; 103 | const interaction = new File(); 104 | interaction.client = this; 105 | this.interactions.set(interaction.name, interaction); 106 | const rest = new REST({version: "9"}).setToken(process.env.TOKEN); 107 | 108 | rest.post( 109 | Routes.applicationGuildCommands(process.env.CLIENT_ID, guildId), 110 | { 111 | body: interaction, 112 | } 113 | ); 114 | }); 115 | } 116 | 117 | /* Load events */ 118 | async loadEvents() { 119 | const evtFile = await sync(resolve("./src/events/**/*.js")); 120 | evtFile.forEach((filepath) => { 121 | const File = require(filepath); 122 | if (!(File.prototype instanceof Event)) return; 123 | const event = new File(); 124 | event.client = this; 125 | this.events.set(event.name, event); 126 | const emitter = event.emitter 127 | ? typeof event.emitter === "string" 128 | ? this[event.emitter] 129 | : emitter 130 | : this; 131 | emitter[event.type ? "once" : "on"](event.name, (...args) => 132 | event.exec(...args) 133 | ); 134 | }); 135 | } 136 | 137 | /* Start the bot */ 138 | async start(token) { 139 | await this.loadEvents(); 140 | await this.loadDatabase(); 141 | return super.login(token); 142 | } 143 | }; 144 | -------------------------------------------------------------------------------- /src/struct/Event.js: -------------------------------------------------------------------------------- 1 | global.Event = module.exports = class Event { 2 | constructor(options) { 3 | this.name = options.name || ""; 4 | this.type = options.once || false; 5 | } 6 | 7 | async exec(...args) { 8 | throw new Error(`${this.name} does not provide exec method !`); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/struct/Interaction.js: -------------------------------------------------------------------------------- 1 | const { Permissions } = require("discord.js"); 2 | global.Interaction = module.exports = class Interaction { 3 | constructor(options) { 4 | this.name = options.name || name; 5 | this.type = options.type || 1; 6 | this.description = 7 | this.type === 1 8 | ? options.description || "No description provided" 9 | : undefined; 10 | this.options = options.options || []; 11 | this.defaultPermission = options.defaultPermission; 12 | this.memberPerms = new Permissions(options.memberPerms).freeze(); 13 | this.clientPerms = new Permissions(options.clientPerms).freeze(); 14 | } 15 | 16 | async exec(...args) { 17 | throw new Error(`${this.name} does not provide exec method !`); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/Logger.js: -------------------------------------------------------------------------------- 1 | const moment = require("moment"); 2 | const chalk = require("chalk"); 3 | const util = require("util"); 4 | 5 | module.exports = class Logger { 6 | static log(content, { color = "blue", tag = "Log" } = {}) { 7 | this.write(content, { color, tag }); 8 | } 9 | 10 | static warn(content, { color = "orange", tag = "Warn" } = {}) { 11 | this.write(content, { color, tag }); 12 | } 13 | 14 | static error(content, { color = "red", tag = "Error" } = {}) { 15 | this.write(content, { color, tag, error: true }); 16 | } 17 | 18 | static write(content, { color = "grey", tag = "Log", error = false } = {}) { 19 | const timestamp = chalk.cyan( 20 | `[${moment().format("DD-MM-YYYY kk:mm:ss")}]:` 21 | ); 22 | const levelTag = chalk.bold(`[${tag}]`); 23 | const text = chalk[color](this.clean(content)); 24 | const stream = error ? process.stderr : process.stdout; 25 | stream.write(`${timestamp} ${levelTag} ${text}\n`); 26 | } 27 | 28 | static clean(item) { 29 | if (typeof item === "string") return item; 30 | const cleaned = util.inspect(item, { depth: Infinity }); 31 | return cleaned; 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/utils/Pagination.js: -------------------------------------------------------------------------------- 1 | const { MessageActionRow, MessageEmbed, MessageButton } = require('discord.js') 2 | 3 | /** 4 | * Creates a pagination embed 5 | * @param {Interaction} interaction 6 | * @param {MessageEmbed[]} pages 7 | * @param {MessageButton[]} buttonList 8 | * @param {number} timeout 9 | * @returns 10 | */ 11 | const paginationEmbed = async ( 12 | interaction, 13 | pages, 14 | buttonList, 15 | timeout = 120000 16 | ) => { 17 | if (!pages) throw new Error("Pages are not given."); 18 | if (!buttonList) throw new Error("Buttons are not given."); 19 | if (buttonList[0].style === "LINK" || buttonList[1].style === "LINK") 20 | throw new Error( 21 | "The button style can't be LINK." 22 | ); 23 | if (buttonList.length !== 2) throw new Error("Need two buttons."); 24 | 25 | let page = 0; 26 | 27 | const row = new MessageActionRow().addComponents(buttonList); 28 | 29 | //has the interaction already been deferred? If not, defer the reply. 30 | if (interaction.deferred === false) { 31 | await interaction.deferReply(); 32 | } 33 | 34 | const curPage = await interaction.editReply({ 35 | embeds: [pages[page].setFooter(`Page ${page + 1} / ${pages.length}`)], 36 | components: [row], 37 | fetchReply: true, 38 | }); 39 | 40 | const filter = (i) => 41 | i.customId === buttonList[0].customId || 42 | i.customId === buttonList[1].customId; 43 | 44 | const collector = await curPage.createMessageComponentCollector({ 45 | filter, 46 | time: timeout, 47 | }); 48 | 49 | collector.on("collect", async (i) => { 50 | switch (i.customId) { 51 | case buttonList[0].customId: 52 | page = page > 0 ? --page : pages.length - 1; 53 | break; 54 | case buttonList[1].customId: 55 | page = page + 1 < pages.length ? ++page : 0; 56 | break; 57 | default: 58 | break; 59 | } 60 | await i.deferUpdate(); 61 | await i.editReply({ 62 | embeds: [pages[page].setFooter(`Page ${page + 1} / ${pages.length}`)], 63 | components: [row], 64 | }); 65 | collector.resetTimer(); 66 | }); 67 | 68 | collector.on("end", () => { 69 | if (!curPage.deleted) { 70 | const disabledRow = new MessageActionRow().addComponents( 71 | buttonList[0].setDisabled(true), 72 | buttonList[1].setDisabled(true) 73 | ); 74 | curPage.edit({ 75 | embeds: [pages[page].setFooter(`Page ${page + 1} / ${pages.length}`)], 76 | components: [disabledRow], 77 | }); 78 | } 79 | }); 80 | 81 | return curPage; 82 | }; 83 | module.exports = paginationEmbed; 84 | -------------------------------------------------------------------------------- /src/utils/Utils.js: -------------------------------------------------------------------------------- 1 | module.exports = class Util { 2 | constructor(client) { 3 | this.client = client; 4 | } 5 | 6 | static formatPerms(perm) { 7 | return perm 8 | .toLowerCase() 9 | .replace(/(^|"|_)(\S)/g, (string) => string.toUpperCase()) 10 | .replace(/_/g, " ") 11 | .replace(/To/g, "to") 12 | .replace(/And/g, "and") 13 | .replace(/Guild/g, "Server") 14 | .replace(/Tts/g, "Text-to-Speech") 15 | .replace(/Use Vad/g, "Use Voice Acitvity"); 16 | } 17 | 18 | static formatArray(array, type = "conjunction") { 19 | return new Intl.ListFormat("en-GB", { style: "short", type: type }).format( 20 | array 21 | ); 22 | } 23 | 24 | static removeDuplicates(arr) { 25 | return [...new Set(arr)]; 26 | } 27 | 28 | static msToSeconds(ms) { 29 | return parseInt(Math.floor(ms / 1000)); 30 | } 31 | 32 | static secondsToMs(s) { 33 | return parseInt(s * 1000); 34 | } 35 | 36 | static readTime(time) { 37 | let s = time % 60; 38 | time -= s; 39 | let m = (time / 60) % 60; 40 | time -= m * 60; 41 | let h = (time / 3600) % 24; 42 | 43 | if (m <= 0) { 44 | return `${s} sec`; 45 | } else if (h <= 0){ 46 | return `${m} min`; 47 | } else { 48 | return `${h}h`; 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | 1. Add anti-invites system 2 | 2. Add bad words system 3 | 3. Add more events to log in events/guild --------------------------------------------------------------------------------