├── .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 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
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
--------------------------------------------------------------------------------