├── .github
├── FUNDING.yml
└── workflows
│ ├── FUNDING.yml
│ └── node.js.yml
├── .replit
├── Dockerfile
├── LICENSE
├── README.md
├── commands
├── admin
│ ├── setlog.js
│ ├── status.js
│ ├── toggle-antilink.js
│ ├── toggle-antispam.js
│ └── toggle-automod.js
├── general
│ ├── avatar.js
│ ├── help.js
│ ├── math.js
│ └── translate.js
├── info
│ ├── botinfo.js
│ ├── channelinfo.js
│ ├── emojiinfo.js
│ ├── ping.js
│ ├── roleinfo.js
│ ├── serverinfo.js
│ ├── uptime.js
│ └── userinfo.js
├── moderation
│ ├── ban.js
│ ├── clear.js
│ ├── clearwarnings.js
│ ├── kick.js
│ ├── mute.js
│ ├── timeout.js
│ ├── unban.js
│ ├── unmute.js
│ ├── warn.js
│ └── warnings.js
└── owner
│ ├── botstatus.js
│ ├── eval.js
│ ├── guilds.js
│ └── reload.js
├── deploy-commands.js
├── events
├── deleteMessage.js
├── interactionCreate.js
├── messageCreate.js
├── messageDelete.js
└── messageUpdate.js
├── index.js
├── models
├── GuildSettings.js
└── Warn.js
├── package-lock.json
├── package.json
├── toggle-antispam.js
└── utils
└── logToChannel.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | patreon: khanmanan
2 | github: khanmanan
3 |
--------------------------------------------------------------------------------
/.github/workflows/FUNDING.yml:
--------------------------------------------------------------------------------
1 | custom:['cwkhan.xyz', 'automodbot.com']
2 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ main ]
9 | pull_request:
10 | branches: [ main ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [12.x]
20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21 |
22 | steps:
23 | - uses: actions/checkout@v2
24 | - name: Use Node.js ${{ matrix.node-version }}
25 | uses: actions/setup-node@v2
26 | with:
27 | node-version: ${{ matrix.node-version }}
28 | - run: npm install
29 |
--------------------------------------------------------------------------------
/.replit:
--------------------------------------------------------------------------------
1 | modules = ["nodejs-20"]
2 | run = "node index.js"
3 |
4 | [nix]
5 | channel = "stable-24_05"
6 | packages = ["run"]
7 |
8 | [deployment]
9 | run = ["sh", "-c", "node index.js"]
10 |
11 | [[ports]]
12 | localPort = 3000
13 | externalPort = 80
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:16.15-alpine3.15
2 |
3 | WORKDIR /app
4 |
5 | COPY package*.json ./
6 |
7 | RUN npm install
8 |
9 | COPY . .
10 |
11 | EXPOSE 8080
12 |
13 | CMD ["npm","run","start"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | NOTE
15 |
16 | Collaborators and all contributors alongwith the copyright holder have the discretion as to submit takedown requests upon any material that is possibly infringing
17 | the license holder's rights.
18 |
19 |
20 | Statement of Purpose
21 |
22 | The laws of most jurisdictions throughout the world automatically confer
23 | exclusive Copyright and Related Rights (defined below) upon the creator
24 | and subsequent owner(s) (each and all, an "owner") of an original work of
25 | authorship and/or a database (each, a "Work").
26 |
27 | Certain owners wish to permanently relinquish those rights to a Work for
28 | the purpose of contributing to a commons of creative, cultural and
29 | scientific works ("Commons") that the public can reliably and without fear
30 | of later claims of infringement build upon, modify, incorporate in other
31 | works, reuse and redistribute as freely as possible in any form whatsoever
32 | and for any purposes, including without limitation commercial purposes.
33 | These owners may contribute to the Commons to promote the ideal of a free
34 | culture and the further production of creative, cultural and scientific
35 | works, or to gain reputation or greater distribution for their Work in
36 | part through the use and efforts of others.
37 |
38 | For these and/or other purposes and motivations, and without any
39 | expectation of additional consideration or compensation, the person
40 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
41 | is an owner of Copyright and Related Rights in the Work, voluntarily
42 | elects to apply CC0 to the Work and publicly distribute the Work under its
43 | terms, with knowledge of his or her Copyright and Related Rights in the
44 | Work and the meaning and intended legal effect of CC0 on those rights.
45 |
46 | 1. Copyright and Related Rights. A Work made available under CC0 may be
47 | protected by copyright and related or neighboring rights ("Copyright and
48 | Related Rights"). Copyright and Related Rights include, but are not
49 | limited to, the following:
50 |
51 | i. the right to reproduce, adapt, distribute, perform, display,
52 | communicate, and translate a Work;
53 | ii. moral rights retained by the original author(s) and/or performer(s);
54 | iii. publicity and privacy rights pertaining to a person's image or
55 | likeness depicted in a Work;
56 | iv. rights protecting against unfair competition in regards to a Work,
57 | subject to the limitations in paragraph 4(a), below;
58 | v. rights protecting the extraction, dissemination, use and reuse of data
59 | in a Work;
60 | vi. database rights (such as those arising under Directive 96/9/EC of the
61 | European Parliament and of the Council of 11 March 1996 on the legal
62 | protection of databases, and under any national implementation
63 | thereof, including any amended or successor version of such
64 | directive); and
65 | vii. other similar, equivalent or corresponding rights throughout the
66 | world based on applicable law or treaty, and any national
67 | implementations thereof.
68 |
69 | 2. Waiver. To the greatest extent permitted by, but not in contravention
70 | of, applicable law, Affirmer hereby overtly, fully, permanently,
71 | irrevocably and unconditionally waives, abandons, and surrenders all of
72 | Affirmer's Copyright and Related Rights and associated claims and causes
73 | of action, whether now known or unknown (including existing as well as
74 | future claims and causes of action), in the Work (i) in all territories
75 | worldwide, (ii) for the maximum duration provided by applicable law or
76 | treaty (including future time extensions), (iii) in any current or future
77 | medium and for any number of copies, and (iv) for any purpose whatsoever,
78 | including without limitation commercial, advertising or promotional
79 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
80 | member of the public at large and to the detriment of Affirmer's heirs and
81 | successors, fully intending that such Waiver shall not be subject to
82 | revocation, rescission, cancellation, termination, or any other legal or
83 | equitable action to disrupt the quiet enjoyment of the Work by the public
84 | as contemplated by Affirmer's express Statement of Purpose.
85 |
86 | 3. Public License Fallback. Should any part of the Waiver for any reason
87 | be judged legally invalid or ineffective under applicable law, then the
88 | Waiver shall be preserved to the maximum extent permitted taking into
89 | account Affirmer's express Statement of Purpose. In addition, to the
90 | extent the Waiver is so judged Affirmer hereby grants to each affected
91 | person a royalty-free, non transferable, non sublicensable, non exclusive,
92 | irrevocable and unconditional license to exercise Affirmer's Copyright and
93 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
94 | maximum duration provided by applicable law or treaty (including future
95 | time extensions), (iii) in any current or future medium and for any number
96 | of copies, and (iv) for any purpose whatsoever, including without
97 | limitation commercial, advertising or promotional purposes (the
98 | "License"). The License shall be deemed effective as of the date CC0 was
99 | applied by Affirmer to the Work. Should any part of the License for any
100 | reason be judged legally invalid or ineffective under applicable law, such
101 | partial invalidity or ineffectiveness shall not invalidate the remainder
102 | of the License, and in such case Affirmer hereby affirms that he or she
103 | will not (i) exercise any of his or her remaining Copyright and Related
104 | Rights in the Work or (ii) assert any associated claims and causes of
105 | action with respect to the Work, in either case contrary to Affirmer's
106 | express Statement of Purpose.
107 |
108 | 4. Limitations and Disclaimers.
109 |
110 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
111 | surrendered, licensed or otherwise affected by this document.
112 | b. Affirmer offers the Work as-is and makes no representations or
113 | warranties of any kind concerning the Work, express, implied,
114 | statutory or otherwise, including without limitation warranties of
115 | title, merchantability, fitness for a particular purpose, non
116 | infringement, or the absence of latent or other defects, accuracy, or
117 | the present or absence of errors, whether or not discoverable, all to
118 | the greatest extent permissible under applicable law.
119 | c. Affirmer disclaims responsibility for clearing rights of other persons
120 | that may apply to the Work or any use thereof, including without
121 | limitation any person's Copyright and Related Rights in the Work.
122 | Further, Affirmer disclaims responsibility for obtaining any necessary
123 | consents, permissions or other rights required for any use of the
124 | Work.
125 | d. Affirmer understands and acknowledges that Creative Commons is not a
126 | party to this document and has no duty or obligation with respect to
127 | this CC0 or use of the Work.
128 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🤖 Automod Bot
2 |
3 | A powerful moderation Discord bot built with Discord.js v14, Mongoose, designed to keep your server clean, safe, and automated.
4 |
5 | 
6 |
7 | ---
8 | [invite the bot](https://discord.com/oauth2/authorize?client_id=1363943273374154842&permissions=8&scope=bot%20applications.Commands)
9 | ## 🚀 Features
10 |
11 | - Slash commands with auto-detection and pagination help menu
12 | - Automod with toggle system per server (anti-link, anti-spam, anti-mention, etc.)
13 | - Database logging (MongoDB + Mongoose)
14 | - Moderation tools: Warn, Kick, Ban, TempBan, Mute, Unmute
15 | - Logging system for actions like message delete/edit
16 | - Web dashboard integration (soon)
17 | - Discord OAuth for authentication (in progress)
18 |
19 | ---
20 |
21 | ## 🛠 Setup
22 |
23 | ### Prerequisites
24 |
25 | - Node.js v18 or higher
26 | - MongoDB URI (local or cloud)
27 | - Discord Bot Token
28 | - Firebase setup (optional)
29 |
30 | ### Installation
31 |
32 | ```bash
33 | git clone https://github.com/Khanmanan/automod-bot.git
34 | cd automod-bot
35 | npm install
36 | ```
37 |
38 | ### Environment Setup
39 |
40 | Create a `.env` file in the root:
41 |
42 | ```env
43 | TOKEN=your_discord_token
44 | MONGO_URI=your_mongo_connection
45 | CLIENT_ID=your_bot_client_id
46 | GUILD_ID=your_guild_id (for testing commands)
47 | ```
48 |
49 | ### Running the Bot
50 |
51 | ```bash
52 | node index.js
53 | ```
54 |
55 | To deploy slash commands globally or per guild:
56 |
57 | ```bash
58 | node deploy.js
59 | ```
60 |
61 | ---
62 |
63 | ## 🧩 Commands
64 |
65 | All commands are organized in the `/commands` folder and auto-loaded by category. Use `/help` to view all.
66 |
67 | - Admin
68 | - Info
69 | - Moderation
70 | - General
71 | - Owner
72 |
73 | ---
74 |
75 | ## 📌 TODO List
76 |
77 | ### 🔧 Core Features
78 |
79 | - [x] Auto-load slash commands by category
80 | - [x] Help command with pagination and buttons
81 | - [x] Logging system for moderation actions
82 | - [x] Toggle-based automod (on/off per rule)
83 | - [x] Rate-limited buttons on help command
84 |
85 | ### 🌐 Web Dashboard
86 |
87 | - [ ] Discord OAuth2 login
88 | - [ ] Guild selector
89 | - [ ] Toggle Automod settings from UI
90 | - [ ] View mod logs and history
91 |
92 | ### 🧪 Future Ideas
93 |
94 | - [ ] Captcha on join (anti-raid)
95 | - [ ] Dashboard theming (Dark )
96 | - [ ] Analytics dashboard (number of mutes/bans per day)
97 | - [ ] Multi-language support
98 |
99 | ---
100 |
Bot support server
101 |
102 | 
103 |
104 |
105 |
106 |
107 |
108 |
109 | ## 👤 Author
110 |
111 | **Khanmanan**
112 | GitHub: [@Khanmanan](https://github.com/Khanmanan)
113 | Bot Repo: [automod-bot](https://github.com/Khanmanan/automod-bot)
114 |
115 | ---
116 |
117 | ## 📜 License
118 |
119 | MIT © 2025 Khanmanan
120 |
--------------------------------------------------------------------------------
/commands/admin/setlog.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const GuildSettings = require('../../models/GuildSettings');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('setlog')
7 | .setDescription('Set the modlog channel')
8 | .addChannelOption(option =>
9 | option.setName('channel').setDescription('Channel for logging').setRequired(true))
10 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
11 |
12 | async execute(interaction) {
13 | const channel = interaction.options.getChannel('channel');
14 | const guildId = interaction.guild.id;
15 |
16 | let settings = await GuildSettings.findOne({ guildId }) || new GuildSettings({ guildId });
17 | settings.logChannelId = channel.id;
18 | await settings.save();
19 |
20 | const embed = new EmbedBuilder()
21 | .setTitle('✅ Log Channel Set')
22 | .setDescription(`Logging will now go to ${channel}`)
23 | .setColor('Green');
24 |
25 | await interaction.reply({ embeds: [embed], ephemeral: true });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/commands/admin/status.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
2 | const GuildSettings = require('../../models/GuildSettings');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('status')
7 | .setDescription('View the current Automod toggle status for this server'),
8 |
9 | async execute(interaction) {
10 | const guildId = interaction.guild.id;
11 |
12 | let settings = await GuildSettings.findOne({ guildId });
13 | if (!settings) {
14 | settings = new GuildSettings({ guildId });
15 | await settings.save();
16 | }
17 |
18 | const status = (flag) => flag ? '✅ Enabled' : '❌ Disabled';
19 |
20 | const embed = new EmbedBuilder()
21 | .setTitle(`⚙️ Automod Status for ${interaction.guild.name}`)
22 | .setColor('Blue')
23 | .addFields(
24 | { name: 'Automod', value: status(settings.automodEnabled), inline: true },
25 | { name: 'Anti-Link', value: status(settings.antiLink), inline: true },
26 | { name: 'Anti-Spam', value: status(settings.antiSpam), inline: true },
27 | { name: 'Anti-GhostPing', value: status(settings.antiGhostPing), inline: true },
28 | )
29 | .setFooter({ text: `Requested by ${interaction.user.tag}`, iconURL: interaction.user.displayAvatarURL() })
30 | .setTimestamp();
31 |
32 | await interaction.reply({ embeds: [embed], ephemeral: true });
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/commands/admin/toggle-antilink.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const GuildSettings = require('../../models/GuildSettings');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('antilink')
7 | .setDescription('Enable or disable anti-link protection.')
8 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
9 |
10 | async execute(interaction) {
11 | const guildId = interaction.guild.id;
12 | let settings = await GuildSettings.findOne({ guildId }) || new GuildSettings({ guildId });
13 |
14 | settings.antiLink = !settings.antiLink;
15 | await settings.save();
16 |
17 | const embed = new EmbedBuilder()
18 | .setTitle('Anti-Link Toggled')
19 | .setDescription(`Anti-link protection is now **${settings.antiLink ? 'enabled ✅' : 'disabled ❌'}**.`)
20 | .setColor(settings.antiLink ? 'Green' : 'Red');
21 |
22 | await interaction.reply({ embeds: [embed], ephemeral: true });
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/commands/admin/toggle-antispam.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const GuildSettings = require('../../models/GuildSettings');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('antispam')
7 | .setDescription('Enable or disable anti-spam protection.')
8 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
9 |
10 | async execute(interaction) {
11 | const guildId = interaction.guild.id;
12 | let settings = await GuildSettings.findOne({ guildId }) || new GuildSettings({ guildId });
13 |
14 | settings.antiSpam = !settings.antiSpam;
15 | await settings.save();
16 |
17 | const embed = new EmbedBuilder()
18 | .setTitle('Anti-Spam Toggled')
19 | .setDescription(`Anti-spam protection is now **${settings.antiSpam ? 'enabled ✅' : 'disabled ❌'}**.`)
20 | .setColor(settings.antiSpam ? 'Green' : 'Red');
21 |
22 | await interaction.reply({ embeds: [embed], ephemeral: true });
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/commands/admin/toggle-automod.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const GuildSettings = require('../../models/GuildSettings');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('toggle-automod')
7 | .setDescription('Enable or disable Automod for this server.')
8 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
9 |
10 | async execute(interaction) {
11 | const guildId = interaction.guild.id;
12 |
13 | let settings = await GuildSettings.findOne({ guildId });
14 | if (!settings) {
15 | settings = new GuildSettings({ guildId });
16 | }
17 |
18 | settings.automodEnabled = !settings.automodEnabled;
19 | await settings.save();
20 |
21 | const status = settings.automodEnabled ? 'enabled ✅' : 'disabled ❌';
22 | const embed = new EmbedBuilder()
23 | .setTitle('Automod Toggled')
24 | .setDescription(`Automod is now **${status}**.`)
25 | .setColor(settings.automodEnabled ? 'Green' : 'Red')
26 | .setTimestamp();
27 |
28 | await interaction.reply({ embeds: [embed], ephemeral: true });
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/commands/general/avatar.js:
--------------------------------------------------------------------------------
1 | // /commands/general/avatar.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('avatar')
7 | .setDescription('Get the avatar of a user.')
8 | .addUserOption(option =>
9 | option.setName('user')
10 | .setDescription('Select a user')
11 | .setRequired(false)),
12 | async execute(interaction) {
13 | const user = interaction.options.getUser('user') || interaction.user;
14 | const member = interaction.guild.members.cache.get(user.id);
15 |
16 | const embed = new EmbedBuilder()
17 | .setTitle(`${user.username}'s Avatar`)
18 | .setImage(user.displayAvatarURL({ dynamic: true, size: 1024 }))
19 | .setColor('Random');
20 |
21 | if (member && member.avatar) {
22 | embed.addFields({ name: 'Server Avatar', value: `[View](${member.displayAvatarURL({ dynamic: true, size: 1024 })})` });
23 | }
24 |
25 | await interaction.reply({ embeds: [embed] });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/commands/general/help.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
2 | const fs = require('fs');
3 | const path = require('path');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('help')
8 | .setDescription('Show all bot commands!'),
9 |
10 | async execute(interaction, client) {
11 | const categories = [];
12 | const commands = [];
13 |
14 | const commandsPath = path.join(__dirname, '..'); // Adjust if needed
15 | const folders = fs.readdirSync(commandsPath);
16 |
17 | for (const folder of folders) {
18 | const folderPath = path.join(commandsPath, folder);
19 | if (fs.lstatSync(folderPath).isDirectory()) {
20 | const files = fs.readdirSync(folderPath).filter(file => file.endsWith('.js'));
21 | const cmds = [];
22 |
23 | for (const file of files) {
24 | const cmd = require(path.join(folderPath, file));
25 | cmds.push({
26 | name: cmd.data?.name || file.replace('.js', ''),
27 | description: cmd.data?.description || "No description provided."
28 | });
29 | }
30 |
31 | categories.push(folder);
32 | commands.push(cmds);
33 | }
34 | }
35 |
36 | const pages = categories.map((cat, idx) => {
37 | return new EmbedBuilder()
38 | .setTitle("Help Menu")
39 | .setDescription(`📌 **Use the buttons below to navigate!**\n\n__**${cat.toUpperCase()} COMMANDS -**__\n\n` +
40 | commands[idx].map(cmd => `**/${cmd.name}**\n${cmd.description}`).join('\n\n')
41 | )
42 | .setColor("#2f3136")
43 | .setFooter({ text: `Page 1/${categories.length} • Support: https://cwkbot.fun/discord` });
44 | });
45 |
46 | let page = 0;
47 | const getPageEmbed = (pageIndex) => {
48 | return pages[pageIndex].setFooter({ text: `Page ${pageIndex + 1}/${pages.length} • Support: https://cwkbot.fun/discord` });
49 | };
50 |
51 | // Buttons
52 | const first = new ButtonBuilder().setCustomId('first').setLabel('⏮ First').setStyle(ButtonStyle.Secondary);
53 | const previous = new ButtonBuilder().setCustomId('previous').setLabel('⬅ Previous').setStyle(ButtonStyle.Primary);
54 | const next = new ButtonBuilder().setCustomId('next').setLabel('Next ➡').setStyle(ButtonStyle.Primary);
55 | const last = new ButtonBuilder().setCustomId('last').setLabel('Last ⏭').setStyle(ButtonStyle.Secondary);
56 |
57 | const row = new ActionRowBuilder().addComponents(first, previous, next, last);
58 |
59 | const msg = await interaction.reply({ embeds: [getPageEmbed(page)], components: [row], ephemeral: true, fetchReply: true });
60 |
61 | const collector = msg.createMessageComponentCollector({ time: 300_000 });
62 |
63 | collector.on('collect', async i => {
64 | if (i.user.id !== interaction.user.id) {
65 | return i.reply({ content: "❌ You can't control this menu!", ephemeral: true });
66 | }
67 |
68 | if (i.customId === 'first') page = 0;
69 | else if (i.customId === 'previous') page = page > 0 ? --page : pages.length - 1;
70 | else if (i.customId === 'next') page = (page + 1) % pages.length;
71 | else if (i.customId === 'last') page = pages.length - 1;
72 |
73 | await i.update({ embeds: [getPageEmbed(page)], components: [row] });
74 | });
75 |
76 | collector.on('end', async () => {
77 | msg.edit({ components: [] }).catch(() => {});
78 | });
79 | }
80 | };
81 |
--------------------------------------------------------------------------------
/commands/general/math.js:
--------------------------------------------------------------------------------
1 | // /commands/general/math.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 | const math = require('mathjs');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('math')
8 | .setDescription('Calculate a math expression.')
9 | .addStringOption(option =>
10 | option.setName('expression')
11 | .setDescription('Enter the math expression')
12 | .setRequired(true)),
13 | async execute(interaction) {
14 | const expr = interaction.options.getString('expression');
15 |
16 | try {
17 | const result = math.evaluate(expr);
18 |
19 | const embed = new EmbedBuilder()
20 | .setTitle('🧮 Math Result')
21 | .addFields(
22 | { name: 'Expression', value: `\`${expr}\`` },
23 | { name: 'Result', value: `\`${result}\`` }
24 | )
25 | .setColor('Blue');
26 |
27 | await interaction.reply({ embeds: [embed] });
28 | } catch (err) {
29 | await interaction.reply({ content: '❌ Invalid expression.', ephemeral: true });
30 | }
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/commands/general/translate.js:
--------------------------------------------------------------------------------
1 | // /commands/general/translate.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 | const translate = require('@vitalets/google-translate-api');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('translate')
8 | .setDescription('Translate text to a different language.')
9 | .addStringOption(option =>
10 | option.setName('text')
11 | .setDescription('Text to translate')
12 | .setRequired(true))
13 | .addStringOption(option =>
14 | option.setName('to')
15 | .setDescription('Language code (e.g. en, fr, hi)')
16 | .setRequired(true)),
17 | async execute(interaction) {
18 | const text = interaction.options.getString('text');
19 | const toLang = interaction.options.getString('to');
20 |
21 | try {
22 | const res = await translate(text, { to: toLang });
23 |
24 | const embed = new EmbedBuilder()
25 | .setTitle('🌐 Translation')
26 | .addFields(
27 | { name: 'Original', value: text },
28 | { name: `Translated (${toLang})`, value: res.text }
29 | )
30 | .setColor('Green');
31 |
32 | await interaction.reply({ embeds: [embed] });
33 | } catch (err) {
34 | await interaction.reply({ content: '❌ Failed to translate.', ephemeral: true });
35 | }
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/commands/info/botinfo.js:
--------------------------------------------------------------------------------
1 | // /commands/info/botinfo.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 | const os = require('os');
4 | const packageJson = require('../../package.json');
5 |
6 | module.exports = {
7 | data: new SlashCommandBuilder()
8 | .setName('botinfo')
9 | .setDescription('Shows detailed information about the bot.'),
10 | async execute(interaction, client) {
11 | const embed = new EmbedBuilder()
12 | .setTitle('🤖 Bot Information')
13 | .setColor('Blurple')
14 | .addFields(
15 | { name: 'Name', value: client.user.username, inline: true },
16 | { name: 'Tag', value: client.user.discriminator, inline: true },
17 | { name: 'Ping', value: `${client.ws.ping}ms`, inline: true },
18 | { name: 'Uptime', value: ``, inline: true },
19 | { name: 'Servers', value: `${client.guilds.cache.size}`, inline: true },
20 | { name: 'Users', value: `${client.users.cache.size}`, inline: true },
21 | { name: 'Node.js', value: `${process.version}`, inline: true },
22 | { name: 'Platform', value: `${os.platform()} (${os.arch()})`, inline: true },
23 | { name: 'RAM Usage', value: `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`, inline: true },
24 | { name: 'Version', value: `v${packageJson.version}`, inline: true }
25 | )
26 | .setFooter({ text: `Made by ${packageJson.author}` });
27 |
28 | await interaction.reply({ embeds: [embed] });
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/commands/info/channelinfo.js:
--------------------------------------------------------------------------------
1 | // /commands/info/channelinfo.js
2 | const { SlashCommandBuilder, EmbedBuilder, ChannelType } = require('discord.js');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('channelinfo')
7 | .setDescription('Displays information about a channel.')
8 | .addChannelOption(option =>
9 | option.setName('channel')
10 | .setDescription('Channel to get info on')
11 | .setRequired(true)),
12 | async execute(interaction) {
13 | const channel = interaction.options.getChannel('channel');
14 |
15 | const embed = new EmbedBuilder()
16 | .setTitle(`📺 Channel Info: ${channel.name}`)
17 | .setColor('Blue')
18 | .addFields(
19 | { name: 'ID', value: channel.id, inline: true },
20 | { name: 'Type', value: ChannelType[channel.type], inline: true },
21 | { name: 'NSFW', value: channel.nsfw ? 'Yes' : 'No', inline: true },
22 | { name: 'Created', value: ``, inline: true }
23 | );
24 |
25 | await interaction.reply({ embeds: [embed] });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/commands/info/emojiinfo.js:
--------------------------------------------------------------------------------
1 | // /commands/info/emojiinfo.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('emojiinfo')
7 | .setDescription('Displays information about a custom emoji.')
8 | .addStringOption(option =>
9 | option.setName('emoji')
10 | .setDescription('Emoji to get info on (e.g. <:smile:1234567890>)')
11 | .setRequired(true)),
12 | async execute(interaction) {
13 | const input = interaction.options.getString('emoji');
14 | const match = input.match(//);
15 |
16 | if (!match) {
17 | return interaction.reply({ content: 'Invalid emoji format.', ephemeral: true });
18 | }
19 |
20 | const emoji = interaction.client.emojis.cache.get(match[1]);
21 | if (!emoji) return interaction.reply({ content: 'Emoji not found in cache.', ephemeral: true });
22 |
23 | const embed = new EmbedBuilder()
24 | .setTitle(`🧩 Emoji Info: ${emoji.name}`)
25 | .setThumbnail(emoji.url)
26 | .setColor('Yellow')
27 | .addFields(
28 | { name: 'ID', value: emoji.id, inline: true },
29 | { name: 'Animated', value: emoji.animated ? 'Yes' : 'No', inline: true },
30 | { name: 'Created', value: ``, inline: true },
31 | { name: 'URL', value: `[Link](${emoji.url})`, inline: false }
32 | );
33 |
34 | await interaction.reply({ embeds: [embed] });
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/commands/info/ping.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/commands/info/roleinfo.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/commands/info/serverinfo.js:
--------------------------------------------------------------------------------
1 | // /commands/info/serverinfo.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('serverinfo')
7 | .setDescription('Displays information about this server.'),
8 | async execute(interaction) {
9 | const { guild } = interaction;
10 |
11 | const embed = new EmbedBuilder()
12 | .setTitle(`🌐 Server Info: ${guild.name}`)
13 | .setThumbnail(guild.iconURL({ dynamic: true }))
14 | .setColor('Green')
15 | .addFields(
16 | { name: 'Server ID', value: guild.id, inline: true },
17 | { name: 'Owner', value: `<@${guild.ownerId}>`, inline: true },
18 | { name: 'Members', value: `${guild.memberCount}`, inline: true },
19 | { name: 'Roles', value: `${guild.roles.cache.size}`, inline: true },
20 | { name: 'Channels', value: `${guild.channels.cache.size}`, inline: true },
21 | { name: 'Created', value: ``, inline: true }
22 | );
23 |
24 | await interaction.reply({ embeds: [embed] });
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/commands/info/uptime.js:
--------------------------------------------------------------------------------
1 | // /commands/info/uptime.js
2 | const { SlashCommandBuilder } = require('discord.js');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('uptime')
7 | .setDescription('Shows how long the bot has been online.'),
8 | async execute(interaction, client) {
9 | const totalSeconds = Math.floor(client.uptime / 1000);
10 | const days = Math.floor(totalSeconds / 86400);
11 | const hours = Math.floor(totalSeconds / 3600) % 24;
12 | const minutes = Math.floor(totalSeconds / 60) % 60;
13 | const seconds = totalSeconds % 60;
14 |
15 | await interaction.reply(`🕒 Uptime: \`${days}d ${hours}h ${minutes}m ${seconds}s\``);
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/commands/info/userinfo.js:
--------------------------------------------------------------------------------
1 | // /commands/info/userinfo.js
2 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('userinfo')
7 | .setDescription('Shows information about a user.')
8 | .addUserOption(option =>
9 | option.setName('target')
10 | .setDescription('User to get info on')
11 | .setRequired(false)),
12 | async execute(interaction) {
13 | const user = interaction.options.getUser('target') || interaction.user;
14 | const member = interaction.guild.members.cache.get(user.id);
15 |
16 | const embed = new EmbedBuilder()
17 | .setTitle(`👤 User Info: ${user.tag}`)
18 | .setThumbnail(user.displayAvatarURL({ dynamic: true }))
19 | .setColor('Orange')
20 | .addFields(
21 | { name: 'User ID', value: user.id, inline: true },
22 | { name: 'Account Created', value: ``, inline: true },
23 | { name: 'Joined Server', value: ``, inline: true },
24 | { name: 'Roles', value: `${member.roles.cache.map(r => r).join(', ')}`, inline: false }
25 | );
26 |
27 | await interaction.reply({ embeds: [embed] });
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/commands/moderation/ban.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const logToChannel = require('../../utils/logToChannel');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('ban')
7 | .setDescription('Ban a member')
8 | .addUserOption(option =>
9 | option.setName('user').setDescription('User to ban').setRequired(true))
10 | .addStringOption(option =>
11 | option.setName('reason').setDescription('Reason').setRequired(false))
12 | .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
13 |
14 | async execute(interaction) {
15 | const user = interaction.options.getUser('user');
16 | const reason = interaction.options.getString('reason') || 'No reason provided';
17 |
18 | try {
19 | await interaction.guild.members.ban(user.id, { reason });
20 |
21 | const embed = new EmbedBuilder()
22 | .setTitle('🔨 Member Banned')
23 | .setDescription(`${user.tag} has been banned.`)
24 | .addFields({ name: 'Reason', value: reason })
25 | .setColor('Red')
26 | .setTimestamp();
27 |
28 | await interaction.reply({ embeds: [embed] });
29 |
30 | await logToChannel(interaction.guild, {
31 | title: '🔨 User Banned',
32 | fields: [
33 | { name: 'User', value: `<@${user.id}>`, inline: true },
34 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true },
35 | { name: 'Reason', value: reason }
36 | ],
37 | color: 'Red'
38 | });
39 | } catch (err) {
40 | await interaction.reply({ content: `Failed to ban user: ${err.message}`, ephemeral: true });
41 | }
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/commands/moderation/clear.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 |
3 | module.exports = {
4 | data: new SlashCommandBuilder()
5 | .setName('clear')
6 | .setDescription('Clear messages from a channel')
7 | .addIntegerOption(option =>
8 | option.setName('amount')
9 | .setDescription('Number of messages to delete')
10 | .setRequired(true))
11 | .setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages),
12 |
13 | async execute(interaction) {
14 | const amount = interaction.options.getInteger('amount');
15 |
16 | if (amount < 1 || amount > 100)
17 | return interaction.reply({ content: 'Please provide a number between 1 and 100.', ephemeral: true });
18 |
19 | try {
20 | const messages = await interaction.channel.bulkDelete(amount, true);
21 | const embed = new EmbedBuilder()
22 | .setTitle('🧹 Messages Cleared')
23 | .setDescription(`Successfully deleted ${messages.size} messages.`)
24 | .setColor('Blue')
25 | .setTimestamp();
26 |
27 | await interaction.reply({ embeds: [embed], ephemeral: true });
28 | } catch (err) {
29 | console.error(err);
30 | interaction.reply({ content: 'Failed to delete messages.', ephemeral: true });
31 | }
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/commands/moderation/clearwarnings.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const Warn = require('../../models/Warn');
3 | const logToChannel = require('../../utils/logToChannel');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('clearwarns')
8 | .setDescription('Clear all warnings for a user')
9 | .addUserOption(option => option.setName('user').setDescription('User to clear warnings for').setRequired(true))
10 | .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
11 |
12 | async execute(interaction) {
13 | const user = interaction.options.getUser('user');
14 | const result = await Warn.findOneAndDelete({ userId: user.id, guildId: interaction.guild.id });
15 |
16 | const embed = new EmbedBuilder()
17 | .setTitle('🧹 Warnings Cleared')
18 | .setDescription(result ? `Cleared all warnings for ${user.tag}` : `No warnings to clear for ${user.tag}`)
19 | .setColor('Green')
20 | .setTimestamp();
21 |
22 | await interaction.reply({ embeds: [embed] });
23 |
24 | await logToChannel(interaction.guild, {
25 | title: '🧹 Warnings Cleared',
26 | fields: [
27 | { name: 'User', value: `<@${user.id}>`, inline: true },
28 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true }
29 | ],
30 | color: 'Green'
31 | });
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/commands/moderation/kick.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const logToChannel = require('../../utils/logToChannel');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('kick')
7 | .setDescription('Kick a member')
8 | .addUserOption(option =>
9 | option.setName('user').setDescription('User to kick').setRequired(true))
10 | .addStringOption(option =>
11 | option.setName('reason').setDescription('Reason').setRequired(false))
12 | .setDefaultMemberPermissions(PermissionFlagsBits.KickMembers),
13 |
14 | async execute(interaction) {
15 | const member = interaction.options.getMember('user');
16 | const reason = interaction.options.getString('reason') || 'No reason provided';
17 |
18 | if (!member.kickable) {
19 | return interaction.reply({ content: 'I cannot kick this user.', ephemeral: true });
20 | }
21 |
22 | await member.kick(reason);
23 |
24 | const embed = new EmbedBuilder()
25 | .setTitle('👢 Member Kicked')
26 | .setDescription(`${member.user.tag} was kicked.`)
27 | .addFields({ name: 'Reason', value: reason })
28 | .setColor('Orange')
29 | .setTimestamp();
30 |
31 | await interaction.reply({ embeds: [embed] });
32 |
33 | await logToChannel(interaction.guild, {
34 | title: '👢 User Kicked',
35 | fields: [
36 | { name: 'User', value: `<@${member.id}>`, inline: true },
37 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true },
38 | { name: 'Reason', value: reason }
39 | ],
40 | color: 'Orange'
41 | });
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/commands/moderation/mute.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 |
3 | module.exports = {
4 | data: new SlashCommandBuilder()
5 | .setName('mute')
6 | .setDescription('Mute a member')
7 | .addUserOption(option =>
8 | option.setName('user')
9 | .setDescription('The member to mute')
10 | .setRequired(true))
11 | .addStringOption(option =>
12 | option.setName('reason')
13 | .setDescription('Reason for mute')
14 | .setRequired(false))
15 | .setDefaultMemberPermissions(PermissionFlagsBits.MuteMembers),
16 |
17 | async execute(interaction) {
18 | const member = interaction.options.getMember('user');
19 | const reason = interaction.options.getString('reason') || 'No reason provided';
20 |
21 | if (!member) return interaction.reply({ content: 'User not found.', ephemeral: true });
22 |
23 | try {
24 | await member.timeout(10 * 60 * 1000, reason); // 10 minutes timeout
25 | const embed = new EmbedBuilder()
26 | .setTitle('🔇 Member Muted')
27 | .setColor('Orange')
28 | .addFields(
29 | { name: 'User', value: `${member.user.tag}`, inline: true },
30 | { name: 'Reason', value: reason, inline: true },
31 | { name: 'Duration', value: '10 minutes', inline: true },
32 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true }
33 | )
34 | .setTimestamp();
35 |
36 | await interaction.reply({ embeds: [embed] });
37 | } catch (err) {
38 | console.error(err);
39 | interaction.reply({ content: 'Failed to mute the user.', ephemeral: true });
40 | }
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/commands/moderation/timeout.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const ms = require('ms');
3 | const logToChannel = require('../../utils/logToChannel');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('timeout')
8 | .setDescription('Timeout a member')
9 | .addUserOption(option =>
10 | option.setName('user').setDescription('User to timeout').setRequired(true))
11 | .addStringOption(option =>
12 | option.setName('duration').setDescription('Time (e.g., 1h, 30m)').setRequired(true))
13 | .addStringOption(option =>
14 | option.setName('reason').setDescription('Reason').setRequired(false))
15 | .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
16 |
17 | async execute(interaction) {
18 | const member = interaction.options.getMember('user');
19 | const duration = ms(interaction.options.getString('duration'));
20 | const reason = interaction.options.getString('reason') || 'No reason provided';
21 |
22 | if (!duration || duration < 10000 || duration > 28 * 24 * 60 * 60 * 1000) {
23 | return interaction.reply({ content: 'Duration must be between 10s and 28d.', ephemeral: true });
24 | }
25 |
26 | try {
27 | await member.timeout(duration, reason);
28 |
29 | const embed = new EmbedBuilder()
30 | .setTitle('⏱️ Member Timed Out')
31 | .addFields(
32 | { name: 'User', value: `<@${member.id}>`, inline: true },
33 | { name: 'Duration', value: interaction.options.getString('duration'), inline: true },
34 | { name: 'Reason', value: reason }
35 | )
36 | .setColor('Blue')
37 | .setTimestamp();
38 |
39 | await interaction.reply({ embeds: [embed] });
40 |
41 | await logToChannel(interaction.guild, {
42 | title: '⏱️ User Timed Out',
43 | fields: [
44 | { name: 'User', value: `<@${member.id}>`, inline: true },
45 | { name: 'Duration', value: interaction.options.getString('duration'), inline: true },
46 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true },
47 | { name: 'Reason', value: reason }
48 | ],
49 | color: 'Blue'
50 | });
51 | } catch (err) {
52 | await interaction.reply({ content: `Timeout failed: ${err.message}`, ephemeral: true });
53 | }
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/commands/moderation/unban.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 | const logToChannel = require('../../utils/logToChannel');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('unban')
7 | .setDescription('Unban a user by ID')
8 | .addStringOption(option =>
9 | option.setName('user_id').setDescription('User ID to unban').setRequired(true))
10 | .addStringOption(option =>
11 | option.setName('reason').setDescription('Reason').setRequired(false))
12 | .setDefaultMemberPermissions(PermissionFlagsBits.BanMembers),
13 |
14 | async execute(interaction) {
15 | const userId = interaction.options.getString('user_id');
16 | const reason = interaction.options.getString('reason') || 'No reason provided';
17 |
18 | try {
19 | await interaction.guild.members.unban(userId, reason);
20 |
21 | const embed = new EmbedBuilder()
22 | .setTitle('🔓 Member Unbanned')
23 | .setDescription(`Unbanned <@${userId}>`)
24 | .addFields({ name: 'Reason', value: reason })
25 | .setColor('Green')
26 | .setTimestamp();
27 |
28 | await interaction.reply({ embeds: [embed] });
29 |
30 | await logToChannel(interaction.guild, {
31 | title: '🔓 User Unbanned',
32 | fields: [
33 | { name: 'User', value: `<@${userId}>`, inline: true },
34 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true },
35 | { name: 'Reason', value: reason }
36 | ],
37 | color: 'Green'
38 | });
39 | } catch (err) {
40 | await interaction.reply({ content: `Unban failed: ${err.message}`, ephemeral: true });
41 | }
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/commands/moderation/unmute.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
2 |
3 | module.exports = {
4 | data: new SlashCommandBuilder()
5 | .setName('unmute')
6 | .setDescription('Unmute a member')
7 | .addUserOption(option =>
8 | option.setName('user')
9 | .setDescription('The member to unmute')
10 | .setRequired(true))
11 | .setDefaultMemberPermissions(PermissionFlagsBits.MuteMembers),
12 |
13 | async execute(interaction) {
14 | const member = interaction.options.getMember('user');
15 |
16 | if (!member) return interaction.reply({ content: 'User not found.', ephemeral: true });
17 |
18 | try {
19 | await member.timeout(null); // Remove timeout
20 | const embed = new EmbedBuilder()
21 | .setTitle('🔊 Member Unmuted')
22 | .setColor('Green')
23 | .addFields(
24 | { name: 'User', value: `${member.user.tag}`, inline: true },
25 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true }
26 | )
27 | .setTimestamp();
28 |
29 | await interaction.reply({ embeds: [embed] });
30 | } catch (err) {
31 | console.error(err);
32 | interaction.reply({ content: 'Failed to unmute the user.', ephemeral: true });
33 | }
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/commands/moderation/warn.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
2 | const Warn = require('../../models/Warn');
3 | const logToChannel = require('../../utils/logToChannel');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('warn')
8 | .setDescription('Warn a user')
9 | .addUserOption(option => option.setName('user').setDescription('User to warn').setRequired(true))
10 | .addStringOption(option => option.setName('reason').setDescription('Reason for the warning').setRequired(true))
11 | .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
12 |
13 | async execute(interaction) {
14 | const user = interaction.options.getUser('user');
15 | const reason = interaction.options.getString('reason');
16 |
17 | let warningData = await Warn.findOne({ userId: user.id, guildId: interaction.guild.id });
18 | if (!warningData) {
19 | warningData = new Warn({
20 | userId: user.id,
21 | guildId: interaction.guild.id,
22 | warnings: []
23 | });
24 | }
25 |
26 | warningData.warnings.push({
27 | modId: interaction.user.id,
28 | reason: reason,
29 | date: new Date()
30 | });
31 |
32 | await warningData.save();
33 |
34 | const embed = new EmbedBuilder()
35 | .setTitle('⚠️ Member Warned')
36 | .setDescription(`Warned ${user.tag} for: **${reason}**`)
37 | .setColor('Orange')
38 | .setTimestamp();
39 |
40 | await interaction.reply({ embeds: [embed] });
41 |
42 | await logToChannel(interaction.guild, {
43 | title: '⚠️ User Warned',
44 | fields: [
45 | { name: 'User', value: `<@${user.id}>`, inline: true },
46 | { name: 'Moderator', value: `<@${interaction.user.id}>`, inline: true },
47 | { name: 'Reason', value: reason }
48 | ],
49 | color: 'Orange'
50 | });
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/commands/moderation/warnings.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js');
2 | const Warn = require('../../models/Warn');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('warnings')
7 | .setDescription('View a user\'s warnings')
8 | .addUserOption(option => option.setName('user').setDescription('User to view warnings').setRequired(true))
9 | .setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
10 |
11 | async execute(interaction) {
12 | const user = interaction.options.getUser('user');
13 | const warnings = await Warn.findOne({ userId: user.id, guildId: interaction.guild.id });
14 |
15 | const embed = new EmbedBuilder()
16 | .setTitle(`📋 Warnings for ${user.tag}`)
17 | .setColor('Yellow')
18 | .setTimestamp();
19 |
20 | if (!warnings || warnings.warnings.length === 0) {
21 | embed.setDescription('No warnings found.');
22 | } else {
23 | warnings.warnings.slice(0, 10).forEach((warn, i) => {
24 | embed.addFields({
25 | name: `⚠️ Warning ${i + 1}`,
26 | value: `**Reason:** ${warn.reason}\n**Moderator:** <@${warn.modId}>\n**Date:** `
27 | });
28 | });
29 | }
30 |
31 | await interaction.reply({ embeds: [embed] });
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/commands/owner/botstatus.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, ActivityType } = require('discord.js');
2 |
3 | module.exports = {
4 | data: new SlashCommandBuilder()
5 | .setName('botstatus')
6 | .setDescription('Update bot\'s presence (Bot Owner only)')
7 | .addStringOption(option =>
8 | option.setName('activity')
9 | .setDescription('Activity text')
10 | .setRequired(true))
11 | .addStringOption(option =>
12 | option.setName('type')
13 | .setDescription('Type: Playing, Watching, Listening')
14 | .addChoices(
15 | { name: 'Playing', value: 'PLAYING' },
16 | { name: 'Watching', value: 'WATCHING' },
17 | { name: 'Listening', value: 'LISTENING' }
18 | )
19 | .setRequired(true)),
20 |
21 | async execute(interaction) {
22 | if (interaction.user.id !== '682981714523586606') return interaction.reply({ content: '❌ Not authorized.', ephemeral: true });
23 |
24 | const activity = interaction.options.getString('activity');
25 | const type = interaction.options.getString('type');
26 |
27 | interaction.client.user.setPresence({
28 | activities: [{ name: activity, type: ActivityType[type] }],
29 | status: 'online'
30 | });
31 |
32 | await interaction.reply({ content: `✅ Updated presence to **${type} ${activity}**`, ephemeral: true });
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/commands/owner/eval.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder } = require('discord.js');
2 | const { inspect } = require('util');
3 |
4 | module.exports = {
5 | data: new SlashCommandBuilder()
6 | .setName('eval')
7 | .setDescription('Evaluates JavaScript code (Bot Owner only)')
8 | .addStringOption(option =>
9 | option.setName('code')
10 | .setDescription('JavaScript code to run')
11 | .setRequired(true)),
12 |
13 | async execute(interaction) {
14 | if (interaction.user.id !== '682981714523586606') return interaction.reply({ content: '❌ You are not authorized.', ephemeral: true });
15 |
16 | const code = interaction.options.getString('code');
17 | try {
18 | let evaled = await eval(code);
19 | if (typeof evaled !== 'string') evaled = inspect(evaled);
20 |
21 | await interaction.reply({ content: `✅ \`\`\`js\n${evaled}\n\`\`\`` });
22 | } catch (err) {
23 | await interaction.reply({ content: `❌ \`\`\`js\n${err}\n\`\`\``, ephemeral: true });
24 | }
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/commands/owner/guilds.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
2 |
3 | module.exports = {
4 | data: new SlashCommandBuilder()
5 | .setName('guilds')
6 | .setDescription('Shows the servers the bot is in (Bot Owner only)'),
7 |
8 | async execute(interaction) {
9 | if (interaction.user.id !== '682981714523586606') return interaction.reply({ content: '❌ You are not authorized.', ephemeral: true });
10 |
11 | const guildList = interaction.client.guilds.cache.map(g => `${g.name} (${g.id})`).join('\n');
12 |
13 | const embed = new EmbedBuilder()
14 | .setTitle('Bot Guilds')
15 | .setDescription(`\`\`\`\n${guildList}\n\`\`\``)
16 | .setColor('#7289DA');
17 |
18 | await interaction.reply({ embeds: [embed], ephemeral: true });
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/commands/owner/reload.js:
--------------------------------------------------------------------------------
1 | const { SlashCommandBuilder } = require('discord.js');
2 | const fs = require('fs');
3 | const path = require('path');
4 |
5 | module.exports = {
6 | data: new SlashCommandBuilder()
7 | .setName('reload')
8 | .setDescription('Reload a command (Bot Owner only)')
9 | .addStringOption(option =>
10 | option.setName('command')
11 | .setDescription('Name of the command to reload')
12 | .setRequired(true)),
13 |
14 | async execute(interaction) {
15 | if (interaction.user.id !== '682981714523586606') return interaction.reply({ content: '❌ You are not authorized.', ephemeral: true });
16 |
17 | const commandName = interaction.options.getString('command').toLowerCase();
18 | const command = interaction.client.commands.get(commandName);
19 |
20 | if (!command) {
21 | return interaction.reply({ content: `❌ Command \`${commandName}\` not found.`, ephemeral: true });
22 | }
23 |
24 | const commandFoldersPath = path.join(__dirname, '../');
25 | const folder = fs.readdirSync(commandFoldersPath).find(folder => fs.existsSync(`${commandFoldersPath}/${folder}/${commandName}.js`));
26 |
27 | if (!folder) return interaction.reply({ content: '❌ Command file not found.', ephemeral: true });
28 |
29 | delete require.cache[require.resolve(`../${folder}/${commandName}.js`)];
30 |
31 | try {
32 | const newCommand = require(`../${folder}/${commandName}.js`);
33 | interaction.client.commands.set(newCommand.data.name, newCommand);
34 | await interaction.reply({ content: `✅ Successfully reloaded \`${commandName}\`.` });
35 | } catch (error) {
36 | console.error(error);
37 | await interaction.reply({ content: `❌ Error while reloading \`${commandName}\`: \n\`${error.message}\``, ephemeral: true });
38 | }
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/deploy-commands.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const fs = require('fs');
3 | const { REST, Routes } = require('discord.js');
4 |
5 | const commands = [];
6 | const ds = './commands';
7 |
8 | fs.readdirSync(ds).forEach(dir => {
9 | const commandFiles = fs.readdirSync(`${ds}/${dir}`).filter(file => file.endsWith('.js'));
10 | for (const file of commandFiles) {
11 | const command = require(`${ds}/${dir}/${file}`);
12 | if (command.data) {
13 | commands.push(command.data.toJSON());
14 | }
15 | }
16 | });
17 |
18 | const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
19 |
20 | (async () => {
21 | try {
22 | console.log('🔁 Deploying slash commands...');
23 |
24 | await rest.put(
25 | Routes.applicationCommands(process.env.CLIENT_ID),
26 | { body: commands }
27 | );
28 |
29 | console.log('✅ Successfully deployed slash commands!');
30 | } catch (error) {
31 | console.error('❌ Failed to deploy commands:', error);
32 | }
33 | })();
34 |
--------------------------------------------------------------------------------
/events/deleteMessage.js:
--------------------------------------------------------------------------------
1 | const GuildSettings = require('../models/GuildSettings');
2 | const { EmbedBuilder } = require('discord.js');
3 |
4 | module.exports = {
5 | name: 'messageDelete',
6 | async execute(message) {
7 | if (!message.guild || message.partial || message.author?.bot) return;
8 |
9 | const settings = await GuildSettings.findOne({ guildId: message.guild.id });
10 | if (!settings?.logChannelId) return;
11 |
12 | const logChannel = message.guild.channels.cache.get(settings.logChannelId);
13 | if (!logChannel) return;
14 |
15 | const embed = new EmbedBuilder()
16 | .setTitle('🗑️ Message Deleted')
17 | .addFields(
18 | { name: 'User', value: `${message.author.tag}`, inline: true },
19 | { name: 'Channel', value: `${message.channel}`, inline: true },
20 | { name: 'Message', value: message.content || '*[No content]*' }
21 | )
22 | .setTimestamp()
23 | .setColor('Red');
24 |
25 | logChannel.send({ embeds: [embed] });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/events/interactionCreate.js:
--------------------------------------------------------------------------------
1 | const { Events } = require('discord.js');
2 |
3 | module.exports = {
4 | name: Events.InteractionCreate,
5 |
6 | async execute(interaction) {
7 | try {
8 | if (interaction.isChatInputCommand()) {
9 | const command = interaction.client.commands.get(interaction.commandName);
10 |
11 | if (!command) {
12 | console.error(`No command matching ${interaction.commandName} was found.`);
13 | return await interaction.reply({ content: '❌ This command is not available.', ephemeral: true });
14 | }
15 |
16 | await command.execute(interaction);
17 |
18 | } else if (interaction.isButton()) {
19 | // Special case: allow message component collectors (like /help) to handle their own buttons
20 | if (interaction.message?.interaction?.user.id === interaction.user.id) return;
21 |
22 | const button = interaction.client.buttons?.get(interaction.customId);
23 | if (!button) {
24 | console.error(`No button handler matching ${interaction.customId} was found.`);
25 | return await interaction.reply({ content: '❌ This button is not working.', ephemeral: true });
26 | }
27 |
28 | await button.execute(interaction);
29 |
30 | } else if (interaction.isStringSelectMenu()) {
31 | const menu = interaction.client.selectMenus?.get(interaction.customId);
32 | if (!menu) {
33 | console.error(`No select menu handler matching ${interaction.customId} was found.`);
34 | return await interaction.reply({ content: '❌ This select menu is broken.', ephemeral: true });
35 | }
36 |
37 | await menu.execute(interaction);
38 |
39 | } else if (interaction.isModalSubmit()) {
40 | const modal = interaction.client.modals?.get(interaction.customId);
41 | if (!modal) {
42 | console.error(`No modal handler matching ${interaction.customId} was found.`);
43 | return await interaction.reply({ content: '❌ This modal is invalid.', ephemeral: true });
44 | }
45 |
46 | await modal.execute(interaction);
47 | }
48 | } catch (error) {
49 | console.error(`Error handling interaction:`, error);
50 |
51 | try {
52 | if (interaction.replied || interaction.deferred) {
53 | await interaction.followUp({ content: '❌ An unexpected error occurred. Please try again later.', ephemeral: true });
54 | } else {
55 | await interaction.reply({ content: '❌ An unexpected error occurred. Please try again later.', ephemeral: true });
56 | }
57 | } catch (err) {
58 | console.error('Failed to send error message:', err);
59 | }
60 | }
61 | },
62 | };
63 |
--------------------------------------------------------------------------------
/events/messageCreate.js:
--------------------------------------------------------------------------------
1 | const GuildSettings = require('../models/GuildSettings');
2 |
3 | const userMessages = new Map();
4 | const linkRegex = /(https?:\/\/[^\s]+)/g;
5 |
6 | module.exports = {
7 | name: 'messageCreate',
8 | async execute(message) {
9 | if (message.author.bot || !message.guild) return;
10 |
11 | const settings = await GuildSettings.findOne({ guildId: message.guild.id });
12 | if (!settings?.automodEnabled) return;
13 |
14 | // Anti-Link
15 | if (settings.antiLink && linkRegex.test(message.content)) {
16 | try {
17 | await message.delete();
18 | await message.channel.send({
19 | content: `🚫 No links allowed here, <@${message.author.id}>!`,
20 | });
21 | } catch (err) {
22 | console.error('Error deleting link message:', err);
23 | }
24 | }
25 |
26 | // Anti-Spam
27 | if (settings.antiSpam) {
28 | const now = Date.now();
29 | const key = `${message.guild.id}-${message.author.id}`;
30 | if (!userMessages.has(key)) userMessages.set(key, []);
31 | const timestamps = userMessages.get(key);
32 |
33 | timestamps.push(now);
34 | const recent = timestamps.filter(ts => now - ts < 5000);
35 | userMessages.set(key, recent);
36 |
37 | if (recent.length >= 5) {
38 | try {
39 | await message.delete();
40 | await message.channel.send({
41 | content: `⚠️ Stop spamming, <@${message.author.id}>!`,
42 | });
43 | } catch (err) {
44 | console.error('Error deleting spam message:', err);
45 | }
46 | }
47 | }
48 | }
49 | };
50 |
--------------------------------------------------------------------------------
/events/messageDelete.js:
--------------------------------------------------------------------------------
1 | const GuildSettings = require('../models/GuildSettings');
2 |
3 | module.exports = {
4 | name: 'messageDelete',
5 | async execute(message) {
6 | if (message.partial || message.author?.bot || !message.guild) return;
7 |
8 | const settings = await GuildSettings.findOne({ guildId: message.guild.id });
9 | if (!settings?.automodEnabled) return;
10 |
11 | if (message.mentions.users.size > 0) {
12 | await message.channel.send({
13 | content: `👻 **Ghost ping detected!**\nUser: <@${message.author.id}>`,
14 | });
15 | }
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/events/messageUpdate.js:
--------------------------------------------------------------------------------
1 | const { EmbedBuilder } = require('discord.js');
2 | const GuildSettings = require('../models/GuildSettings');
3 |
4 | module.exports = {
5 | name: 'messageUpdate',
6 | async execute(oldMessage, newMessage) {
7 | if (!newMessage.guild || newMessage.author?.bot || oldMessage.content === newMessage.content) return;
8 |
9 | const settings = await GuildSettings.findOne({ guildId: newMessage.guild.id });
10 | if (!settings?.logChannelId) return;
11 |
12 | const logChannel = newMessage.guild.channels.cache.get(settings.logChannelId);
13 | if (!logChannel) return;
14 |
15 | const embed = new EmbedBuilder()
16 | .setTitle('✏️ Message Edited')
17 | .addFields(
18 | { name: 'User', value: `${newMessage.author.tag}`, inline: true },
19 | { name: 'Channel', value: `${newMessage.channel}`, inline: true },
20 | { name: 'Before', value: oldMessage.content || '*[No content]*' },
21 | { name: 'After', value: newMessage.content || '*[No content]*' }
22 | )
23 | .setTimestamp()
24 | .setColor('Orange');
25 |
26 | logChannel.send({ embeds: [embed] });
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const { Client, GatewayIntentBits, Collection, Partials, EmbedBuilder } = require('discord.js');
3 | const fs = require('fs');
4 | const mongoose = require('mongoose');
5 | const path = require('path');
6 | const express = require('express');
7 | const deploy = require ('./deploy-commands.js')
8 | const client = new Client({
9 | intents: [
10 | GatewayIntentBits.Guilds,
11 | GatewayIntentBits.GuildMessages,
12 | GatewayIntentBits.MessageContent,
13 | GatewayIntentBits.GuildMembers,
14 | GatewayIntentBits.GuildModeration,
15 | ],
16 | partials: [Partials.Message, Partials.Channel, Partials.GuildMember],
17 | });
18 |
19 | client.commands = new Collection();
20 |
21 | // Load commands
22 | const commandsPath = path.join(__dirname, 'commands');
23 | fs.readdirSync(commandsPath).forEach(dir => {
24 | const commandFiles = fs.readdirSync(path.join(commandsPath, dir)).filter(file => file.endsWith('.js'));
25 | for (const file of commandFiles) {
26 | const command = require(path.join(commandsPath, dir, file));
27 | if (command.data && command.execute) {
28 | client.commands.set(command.data.name, command);
29 | }
30 | }
31 | });
32 |
33 | // Status event
34 | client.once('ready', () => {
35 | console.log(`✅ Bot ready as ${client.user.tag}`);
36 | client.user.setPresence({
37 | activities: [{ name: 'your server 👀', type: 3 }],
38 | status: 'online'
39 | });
40 | });
41 |
42 | // Clean interaction handler
43 | client.on('interactionCreate', async interaction => {
44 | if (interaction.isChatInputCommand()) {
45 | const command = client.commands.get(interaction.commandName);
46 | if (!command) return;
47 |
48 | try {
49 | await command.execute(interaction, client);
50 | } catch (error) {
51 | console.error(`Error executing command ${interaction.commandName}:`, error);
52 | const errorEmbed = new EmbedBuilder()
53 | .setTitle('❌ Command Error')
54 | .setDescription('There was an error executing this command.')
55 | .setColor('Red');
56 |
57 | if (interaction.replied || interaction.deferred) {
58 | await interaction.followUp({ embeds: [errorEmbed], ephemeral: true });
59 | } else {
60 | await interaction.reply({ embeds: [errorEmbed], ephemeral: true });
61 | }
62 | }
63 | }
64 |
65 | // Handle other types of interactions here if needed
66 | // Example: button/menu interactions can go here
67 | });
68 |
69 | // MongoDB connection
70 | mongoose.connect(process.env.MONGO_URI, {
71 | useNewUrlParser: true,
72 | useUnifiedTopology: true,
73 | }).then(() => {
74 | console.log('✅ Connected to MongoDB');
75 | client.login(process.env.DISCORD_TOKEN);
76 | }).catch(err => {
77 | console.error('❌ MongoDB connection error:', err);
78 | });
79 |
80 | // Event loader
81 | const eventsPath = path.join(__dirname, 'events');
82 | if (fs.existsSync(eventsPath)) {
83 | const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
84 |
85 | for (const file of eventFiles) {
86 | const event = require(path.join(eventsPath, file));
87 | if (event.once) {
88 | client.once(event.name, (...args) => event.execute(...args, client));
89 | } else {
90 | client.on(event.name, (...args) => event.execute(...args, client));
91 | }
92 | }
93 | }
94 |
95 | // KeepAlive server for Replit
96 | const app = express();
97 | app.get('/', (req, res) => {
98 | res.send('Bot is alive!');
99 | });
100 | const PORT = process.env.PORT || 3000;
101 | app.listen(PORT, () => {
102 | console.log(`[+] KeepAlive server running on port ${PORT}`);
103 | });
104 |
105 | // Error catcher
106 | process.on('unhandledRejection', error => {
107 | console.error('Unhandled promise rejection:', error);
108 | });
109 |
--------------------------------------------------------------------------------
/models/GuildSettings.js:
--------------------------------------------------------------------------------
1 | const { Schema, model } = require('mongoose');
2 |
3 | const settingsSchema = new Schema({
4 | guildId: String,
5 | automodEnabled: { type: Boolean, default: false },
6 | antiLink: { type: Boolean, default: false },
7 | antiSpam: { type: Boolean, default: false },
8 | antiGhostPing: { type: Boolean, default: false },
9 | logChannelId: { type: String, default: null },
10 | });
11 |
12 | module.exports = model('GuildSettings', settingsSchema);
13 |
--------------------------------------------------------------------------------
/models/Warn.js:
--------------------------------------------------------------------------------
1 | const { Schema, model, models } = require('mongoose');
2 |
3 | const warnSchema = new Schema({
4 | userId: String,
5 | guildId: String,
6 | warnings: [
7 | {
8 | modId: String,
9 | reason: String,
10 | date: { type: Date, default: Date.now }
11 | }
12 | ]
13 | });
14 |
15 | // ✅ Only compile if it doesn't already exist
16 | module.exports = models.Warning || model('Warning', warnSchema);
17 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "automod-bot",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "automod-bot",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "discord.js": "^14.13.0",
13 | "dotenv": "^16.3.1",
14 | "moment": "^2.29.4",
15 | "mongoose": "^7.6.1"
16 | }
17 | },
18 | "node_modules/@discordjs/builders": {
19 | "version": "1.10.1",
20 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.10.1.tgz",
21 | "integrity": "sha512-OWo1fY4ztL1/M/DUyRPShB4d/EzVfuUvPTRRHRIt/YxBrUYSz0a+JicD5F5zHFoNs2oTuWavxCOVFV1UljHTng==",
22 | "license": "Apache-2.0",
23 | "dependencies": {
24 | "@discordjs/formatters": "^0.6.0",
25 | "@discordjs/util": "^1.1.1",
26 | "@sapphire/shapeshift": "^4.0.0",
27 | "discord-api-types": "^0.37.119",
28 | "fast-deep-equal": "^3.1.3",
29 | "ts-mixer": "^6.0.4",
30 | "tslib": "^2.6.3"
31 | },
32 | "engines": {
33 | "node": ">=16.11.0"
34 | },
35 | "funding": {
36 | "url": "https://github.com/discordjs/discord.js?sponsor"
37 | }
38 | },
39 | "node_modules/@discordjs/collection": {
40 | "version": "1.5.3",
41 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
42 | "integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
43 | "license": "Apache-2.0",
44 | "engines": {
45 | "node": ">=16.11.0"
46 | }
47 | },
48 | "node_modules/@discordjs/formatters": {
49 | "version": "0.6.0",
50 | "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.0.tgz",
51 | "integrity": "sha512-YIruKw4UILt/ivO4uISmrGq2GdMY6EkoTtD0oS0GvkJFRZbTSdPhzYiUILbJ/QslsvC9H9nTgGgnarnIl4jMfw==",
52 | "license": "Apache-2.0",
53 | "dependencies": {
54 | "discord-api-types": "^0.37.114"
55 | },
56 | "engines": {
57 | "node": ">=16.11.0"
58 | },
59 | "funding": {
60 | "url": "https://github.com/discordjs/discord.js?sponsor"
61 | }
62 | },
63 | "node_modules/@discordjs/rest": {
64 | "version": "2.4.3",
65 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.3.tgz",
66 | "integrity": "sha512-+SO4RKvWsM+y8uFHgYQrcTl/3+cY02uQOH7/7bKbVZsTfrfpoE62o5p+mmV+s7FVhTX82/kQUGGbu4YlV60RtA==",
67 | "license": "Apache-2.0",
68 | "dependencies": {
69 | "@discordjs/collection": "^2.1.1",
70 | "@discordjs/util": "^1.1.1",
71 | "@sapphire/async-queue": "^1.5.3",
72 | "@sapphire/snowflake": "^3.5.3",
73 | "@vladfrangu/async_event_emitter": "^2.4.6",
74 | "discord-api-types": "^0.37.119",
75 | "magic-bytes.js": "^1.10.0",
76 | "tslib": "^2.6.3",
77 | "undici": "6.21.1"
78 | },
79 | "engines": {
80 | "node": ">=18"
81 | },
82 | "funding": {
83 | "url": "https://github.com/discordjs/discord.js?sponsor"
84 | }
85 | },
86 | "node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
87 | "version": "2.1.1",
88 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
89 | "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
90 | "license": "Apache-2.0",
91 | "engines": {
92 | "node": ">=18"
93 | },
94 | "funding": {
95 | "url": "https://github.com/discordjs/discord.js?sponsor"
96 | }
97 | },
98 | "node_modules/@discordjs/util": {
99 | "version": "1.1.1",
100 | "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
101 | "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
102 | "license": "Apache-2.0",
103 | "engines": {
104 | "node": ">=18"
105 | },
106 | "funding": {
107 | "url": "https://github.com/discordjs/discord.js?sponsor"
108 | }
109 | },
110 | "node_modules/@discordjs/ws": {
111 | "version": "1.2.1",
112 | "resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.1.tgz",
113 | "integrity": "sha512-PBvenhZG56a6tMWF/f4P6f4GxZKJTBG95n7aiGSPTnodmz4N5g60t79rSIAq7ywMbv8A4jFtexMruH+oe51aQQ==",
114 | "license": "Apache-2.0",
115 | "dependencies": {
116 | "@discordjs/collection": "^2.1.0",
117 | "@discordjs/rest": "^2.4.3",
118 | "@discordjs/util": "^1.1.0",
119 | "@sapphire/async-queue": "^1.5.2",
120 | "@types/ws": "^8.5.10",
121 | "@vladfrangu/async_event_emitter": "^2.2.4",
122 | "discord-api-types": "^0.37.119",
123 | "tslib": "^2.6.2",
124 | "ws": "^8.17.0"
125 | },
126 | "engines": {
127 | "node": ">=16.11.0"
128 | },
129 | "funding": {
130 | "url": "https://github.com/discordjs/discord.js?sponsor"
131 | }
132 | },
133 | "node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
134 | "version": "2.1.1",
135 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
136 | "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
137 | "license": "Apache-2.0",
138 | "engines": {
139 | "node": ">=18"
140 | },
141 | "funding": {
142 | "url": "https://github.com/discordjs/discord.js?sponsor"
143 | }
144 | },
145 | "node_modules/@isaacs/cliui": {
146 | "version": "8.0.2",
147 | "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
148 | "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
149 | "license": "ISC",
150 | "dependencies": {
151 | "string-width": "^5.1.2",
152 | "string-width-cjs": "npm:string-width@^4.2.0",
153 | "strip-ansi": "^7.0.1",
154 | "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
155 | "wrap-ansi": "^8.1.0",
156 | "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
157 | },
158 | "engines": {
159 | "node": ">=12"
160 | }
161 | },
162 | "node_modules/@mongodb-js/saslprep": {
163 | "version": "1.2.2",
164 | "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.2.tgz",
165 | "integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==",
166 | "license": "MIT",
167 | "optional": true,
168 | "dependencies": {
169 | "sparse-bitfield": "^3.0.3"
170 | }
171 | },
172 | "node_modules/@sapphire/async-queue": {
173 | "version": "1.5.5",
174 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
175 | "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==",
176 | "license": "MIT",
177 | "engines": {
178 | "node": ">=v14.0.0",
179 | "npm": ">=7.0.0"
180 | }
181 | },
182 | "node_modules/@sapphire/shapeshift": {
183 | "version": "4.0.0",
184 | "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz",
185 | "integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==",
186 | "license": "MIT",
187 | "dependencies": {
188 | "fast-deep-equal": "^3.1.3",
189 | "lodash": "^4.17.21"
190 | },
191 | "engines": {
192 | "node": ">=v16"
193 | }
194 | },
195 | "node_modules/@sapphire/snowflake": {
196 | "version": "3.5.3",
197 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
198 | "integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
199 | "license": "MIT",
200 | "engines": {
201 | "node": ">=v14.0.0",
202 | "npm": ">=7.0.0"
203 | }
204 | },
205 | "node_modules/@types/node": {
206 | "version": "22.14.1",
207 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
208 | "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
209 | "license": "MIT",
210 | "dependencies": {
211 | "undici-types": "~6.21.0"
212 | }
213 | },
214 | "node_modules/@types/webidl-conversions": {
215 | "version": "7.0.3",
216 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
217 | "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
218 | "license": "MIT"
219 | },
220 | "node_modules/@types/whatwg-url": {
221 | "version": "8.2.2",
222 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
223 | "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
224 | "license": "MIT",
225 | "dependencies": {
226 | "@types/node": "*",
227 | "@types/webidl-conversions": "*"
228 | }
229 | },
230 | "node_modules/@types/ws": {
231 | "version": "8.18.1",
232 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
233 | "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
234 | "license": "MIT",
235 | "dependencies": {
236 | "@types/node": "*"
237 | }
238 | },
239 | "node_modules/@vladfrangu/async_event_emitter": {
240 | "version": "2.4.6",
241 | "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
242 | "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
243 | "license": "MIT",
244 | "engines": {
245 | "node": ">=v14.0.0",
246 | "npm": ">=7.0.0"
247 | }
248 | },
249 | "node_modules/ansi-regex": {
250 | "version": "6.1.0",
251 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
252 | "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
253 | "license": "MIT",
254 | "engines": {
255 | "node": ">=12"
256 | },
257 | "funding": {
258 | "url": "https://github.com/chalk/ansi-regex?sponsor=1"
259 | }
260 | },
261 | "node_modules/ansi-styles": {
262 | "version": "6.2.1",
263 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
264 | "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
265 | "license": "MIT",
266 | "engines": {
267 | "node": ">=12"
268 | },
269 | "funding": {
270 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
271 | }
272 | },
273 | "node_modules/balanced-match": {
274 | "version": "1.0.2",
275 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
276 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
277 | "license": "MIT"
278 | },
279 | "node_modules/brace-expansion": {
280 | "version": "2.0.1",
281 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
282 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
283 | "license": "MIT",
284 | "dependencies": {
285 | "balanced-match": "^1.0.0"
286 | }
287 | },
288 | "node_modules/bson": {
289 | "version": "5.5.1",
290 | "resolved": "https://registry.npmjs.org/bson/-/bson-5.5.1.tgz",
291 | "integrity": "sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==",
292 | "license": "Apache-2.0",
293 | "engines": {
294 | "node": ">=14.20.1"
295 | }
296 | },
297 | "node_modules/color-convert": {
298 | "version": "2.0.1",
299 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
300 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
301 | "license": "MIT",
302 | "dependencies": {
303 | "color-name": "~1.1.4"
304 | },
305 | "engines": {
306 | "node": ">=7.0.0"
307 | }
308 | },
309 | "node_modules/color-name": {
310 | "version": "1.1.4",
311 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
312 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
313 | "license": "MIT"
314 | },
315 | "node_modules/cross-spawn": {
316 | "version": "7.0.6",
317 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
318 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
319 | "license": "MIT",
320 | "dependencies": {
321 | "path-key": "^3.1.0",
322 | "shebang-command": "^2.0.0",
323 | "which": "^2.0.1"
324 | },
325 | "engines": {
326 | "node": ">= 8"
327 | }
328 | },
329 | "node_modules/debug": {
330 | "version": "4.4.0",
331 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
332 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
333 | "license": "MIT",
334 | "dependencies": {
335 | "ms": "^2.1.3"
336 | },
337 | "engines": {
338 | "node": ">=6.0"
339 | },
340 | "peerDependenciesMeta": {
341 | "supports-color": {
342 | "optional": true
343 | }
344 | }
345 | },
346 | "node_modules/discord-api-types": {
347 | "version": "0.37.120",
348 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.120.tgz",
349 | "integrity": "sha512-7xpNK0EiWjjDFp2nAhHXezE4OUWm7s1zhc/UXXN6hnFFU8dfoPHgV0Hx0RPiCa3ILRpdeh152icc68DGCyXYIw==",
350 | "license": "MIT"
351 | },
352 | "node_modules/discord.js": {
353 | "version": "14.18.0",
354 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.18.0.tgz",
355 | "integrity": "sha512-SvU5kVUvwunQhN2/+0t55QW/1EHfB1lp0TtLZUSXVHDmyHTrdOj5LRKdR0zLcybaA15F+NtdWuWmGOX9lE+CAw==",
356 | "license": "Apache-2.0",
357 | "dependencies": {
358 | "@discordjs/builders": "^1.10.1",
359 | "@discordjs/collection": "1.5.3",
360 | "@discordjs/formatters": "^0.6.0",
361 | "@discordjs/rest": "^2.4.3",
362 | "@discordjs/util": "^1.1.1",
363 | "@discordjs/ws": "^1.2.1",
364 | "@sapphire/snowflake": "3.5.3",
365 | "discord-api-types": "^0.37.119",
366 | "fast-deep-equal": "3.1.3",
367 | "lodash.snakecase": "4.1.1",
368 | "tslib": "^2.6.3",
369 | "undici": "6.21.1"
370 | },
371 | "engines": {
372 | "node": ">=18"
373 | },
374 | "funding": {
375 | "url": "https://github.com/discordjs/discord.js?sponsor"
376 | }
377 | },
378 | "node_modules/dotenv": {
379 | "version": "16.5.0",
380 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
381 | "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
382 | "license": "BSD-2-Clause",
383 | "engines": {
384 | "node": ">=12"
385 | },
386 | "funding": {
387 | "url": "https://dotenvx.com"
388 | }
389 | },
390 | "node_modules/eastasianwidth": {
391 | "version": "0.2.0",
392 | "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
393 | "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
394 | "license": "MIT"
395 | },
396 | "node_modules/emoji-regex": {
397 | "version": "9.2.2",
398 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
399 | "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
400 | "license": "MIT"
401 | },
402 | "node_modules/fast-deep-equal": {
403 | "version": "3.1.3",
404 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
405 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
406 | "license": "MIT"
407 | },
408 | "node_modules/foreground-child": {
409 | "version": "3.3.1",
410 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
411 | "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
412 | "license": "ISC",
413 | "dependencies": {
414 | "cross-spawn": "^7.0.6",
415 | "signal-exit": "^4.0.1"
416 | },
417 | "engines": {
418 | "node": ">=14"
419 | },
420 | "funding": {
421 | "url": "https://github.com/sponsors/isaacs"
422 | }
423 | },
424 | "node_modules/glob": {
425 | "version": "11.0.1",
426 | "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
427 | "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
428 | "license": "ISC",
429 | "dependencies": {
430 | "foreground-child": "^3.1.0",
431 | "jackspeak": "^4.0.1",
432 | "minimatch": "^10.0.0",
433 | "minipass": "^7.1.2",
434 | "package-json-from-dist": "^1.0.0",
435 | "path-scurry": "^2.0.0"
436 | },
437 | "bin": {
438 | "glob": "dist/esm/bin.mjs"
439 | },
440 | "engines": {
441 | "node": "20 || >=22"
442 | },
443 | "funding": {
444 | "url": "https://github.com/sponsors/isaacs"
445 | }
446 | },
447 | "node_modules/ip-address": {
448 | "version": "9.0.5",
449 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
450 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
451 | "license": "MIT",
452 | "dependencies": {
453 | "jsbn": "1.1.0",
454 | "sprintf-js": "^1.1.3"
455 | },
456 | "engines": {
457 | "node": ">= 12"
458 | }
459 | },
460 | "node_modules/is-fullwidth-code-point": {
461 | "version": "3.0.0",
462 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
463 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
464 | "license": "MIT",
465 | "engines": {
466 | "node": ">=8"
467 | }
468 | },
469 | "node_modules/isexe": {
470 | "version": "2.0.0",
471 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
472 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
473 | "license": "ISC"
474 | },
475 | "node_modules/jackspeak": {
476 | "version": "4.1.0",
477 | "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
478 | "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==",
479 | "license": "BlueOak-1.0.0",
480 | "dependencies": {
481 | "@isaacs/cliui": "^8.0.2"
482 | },
483 | "engines": {
484 | "node": "20 || >=22"
485 | },
486 | "funding": {
487 | "url": "https://github.com/sponsors/isaacs"
488 | }
489 | },
490 | "node_modules/jsbn": {
491 | "version": "1.1.0",
492 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
493 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
494 | "license": "MIT"
495 | },
496 | "node_modules/kareem": {
497 | "version": "2.5.1",
498 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
499 | "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
500 | "license": "Apache-2.0",
501 | "engines": {
502 | "node": ">=12.0.0"
503 | }
504 | },
505 | "node_modules/lodash": {
506 | "version": "4.17.21",
507 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
508 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
509 | "license": "MIT"
510 | },
511 | "node_modules/lodash.snakecase": {
512 | "version": "4.1.1",
513 | "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
514 | "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
515 | "license": "MIT"
516 | },
517 | "node_modules/lru-cache": {
518 | "version": "11.1.0",
519 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
520 | "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
521 | "license": "ISC",
522 | "engines": {
523 | "node": "20 || >=22"
524 | }
525 | },
526 | "node_modules/magic-bytes.js": {
527 | "version": "1.11.0",
528 | "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.11.0.tgz",
529 | "integrity": "sha512-nVmadqN9gam80tdnn74qjFCKgldwzv1+96XmeCvR3bY7wNn2PjHMnRakOWC6+32g133vgZOjUiYgswIxohffzA==",
530 | "license": "MIT",
531 | "dependencies": {
532 | "rimraf": "^6.0.1"
533 | }
534 | },
535 | "node_modules/memory-pager": {
536 | "version": "1.5.0",
537 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
538 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
539 | "license": "MIT",
540 | "optional": true
541 | },
542 | "node_modules/minimatch": {
543 | "version": "10.0.1",
544 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
545 | "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
546 | "license": "ISC",
547 | "dependencies": {
548 | "brace-expansion": "^2.0.1"
549 | },
550 | "engines": {
551 | "node": "20 || >=22"
552 | },
553 | "funding": {
554 | "url": "https://github.com/sponsors/isaacs"
555 | }
556 | },
557 | "node_modules/minipass": {
558 | "version": "7.1.2",
559 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
560 | "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
561 | "license": "ISC",
562 | "engines": {
563 | "node": ">=16 || 14 >=14.17"
564 | }
565 | },
566 | "node_modules/moment": {
567 | "version": "2.30.1",
568 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
569 | "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
570 | "license": "MIT",
571 | "engines": {
572 | "node": "*"
573 | }
574 | },
575 | "node_modules/mongodb": {
576 | "version": "5.9.2",
577 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz",
578 | "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==",
579 | "license": "Apache-2.0",
580 | "dependencies": {
581 | "bson": "^5.5.0",
582 | "mongodb-connection-string-url": "^2.6.0",
583 | "socks": "^2.7.1"
584 | },
585 | "engines": {
586 | "node": ">=14.20.1"
587 | },
588 | "optionalDependencies": {
589 | "@mongodb-js/saslprep": "^1.1.0"
590 | },
591 | "peerDependencies": {
592 | "@aws-sdk/credential-providers": "^3.188.0",
593 | "@mongodb-js/zstd": "^1.0.0",
594 | "kerberos": "^1.0.0 || ^2.0.0",
595 | "mongodb-client-encryption": ">=2.3.0 <3",
596 | "snappy": "^7.2.2"
597 | },
598 | "peerDependenciesMeta": {
599 | "@aws-sdk/credential-providers": {
600 | "optional": true
601 | },
602 | "@mongodb-js/zstd": {
603 | "optional": true
604 | },
605 | "kerberos": {
606 | "optional": true
607 | },
608 | "mongodb-client-encryption": {
609 | "optional": true
610 | },
611 | "snappy": {
612 | "optional": true
613 | }
614 | }
615 | },
616 | "node_modules/mongodb-connection-string-url": {
617 | "version": "2.6.0",
618 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
619 | "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
620 | "license": "Apache-2.0",
621 | "dependencies": {
622 | "@types/whatwg-url": "^8.2.1",
623 | "whatwg-url": "^11.0.0"
624 | }
625 | },
626 | "node_modules/mongoose": {
627 | "version": "7.8.6",
628 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.8.6.tgz",
629 | "integrity": "sha512-1oVPRHvcmPVwk/zeSTEzayzQEVeYQM1D5zrkLsttfNNB7pPRUmkKeFu6gpbvyEswOuZLrWJjqB8kSTY+k2AZOA==",
630 | "license": "MIT",
631 | "dependencies": {
632 | "bson": "^5.5.0",
633 | "kareem": "2.5.1",
634 | "mongodb": "5.9.2",
635 | "mpath": "0.9.0",
636 | "mquery": "5.0.0",
637 | "ms": "2.1.3",
638 | "sift": "16.0.1"
639 | },
640 | "engines": {
641 | "node": ">=14.20.1"
642 | },
643 | "funding": {
644 | "type": "opencollective",
645 | "url": "https://opencollective.com/mongoose"
646 | }
647 | },
648 | "node_modules/mpath": {
649 | "version": "0.9.0",
650 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
651 | "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
652 | "license": "MIT",
653 | "engines": {
654 | "node": ">=4.0.0"
655 | }
656 | },
657 | "node_modules/mquery": {
658 | "version": "5.0.0",
659 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
660 | "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
661 | "license": "MIT",
662 | "dependencies": {
663 | "debug": "4.x"
664 | },
665 | "engines": {
666 | "node": ">=14.0.0"
667 | }
668 | },
669 | "node_modules/ms": {
670 | "version": "2.1.3",
671 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
672 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
673 | "license": "MIT"
674 | },
675 | "node_modules/package-json-from-dist": {
676 | "version": "1.0.1",
677 | "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
678 | "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
679 | "license": "BlueOak-1.0.0"
680 | },
681 | "node_modules/path-key": {
682 | "version": "3.1.1",
683 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
684 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
685 | "license": "MIT",
686 | "engines": {
687 | "node": ">=8"
688 | }
689 | },
690 | "node_modules/path-scurry": {
691 | "version": "2.0.0",
692 | "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
693 | "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
694 | "license": "BlueOak-1.0.0",
695 | "dependencies": {
696 | "lru-cache": "^11.0.0",
697 | "minipass": "^7.1.2"
698 | },
699 | "engines": {
700 | "node": "20 || >=22"
701 | },
702 | "funding": {
703 | "url": "https://github.com/sponsors/isaacs"
704 | }
705 | },
706 | "node_modules/punycode": {
707 | "version": "2.3.1",
708 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
709 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
710 | "license": "MIT",
711 | "engines": {
712 | "node": ">=6"
713 | }
714 | },
715 | "node_modules/rimraf": {
716 | "version": "6.0.1",
717 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
718 | "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
719 | "license": "ISC",
720 | "dependencies": {
721 | "glob": "^11.0.0",
722 | "package-json-from-dist": "^1.0.0"
723 | },
724 | "bin": {
725 | "rimraf": "dist/esm/bin.mjs"
726 | },
727 | "engines": {
728 | "node": "20 || >=22"
729 | },
730 | "funding": {
731 | "url": "https://github.com/sponsors/isaacs"
732 | }
733 | },
734 | "node_modules/shebang-command": {
735 | "version": "2.0.0",
736 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
737 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
738 | "license": "MIT",
739 | "dependencies": {
740 | "shebang-regex": "^3.0.0"
741 | },
742 | "engines": {
743 | "node": ">=8"
744 | }
745 | },
746 | "node_modules/shebang-regex": {
747 | "version": "3.0.0",
748 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
749 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
750 | "license": "MIT",
751 | "engines": {
752 | "node": ">=8"
753 | }
754 | },
755 | "node_modules/sift": {
756 | "version": "16.0.1",
757 | "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
758 | "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==",
759 | "license": "MIT"
760 | },
761 | "node_modules/signal-exit": {
762 | "version": "4.1.0",
763 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
764 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
765 | "license": "ISC",
766 | "engines": {
767 | "node": ">=14"
768 | },
769 | "funding": {
770 | "url": "https://github.com/sponsors/isaacs"
771 | }
772 | },
773 | "node_modules/smart-buffer": {
774 | "version": "4.2.0",
775 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
776 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
777 | "license": "MIT",
778 | "engines": {
779 | "node": ">= 6.0.0",
780 | "npm": ">= 3.0.0"
781 | }
782 | },
783 | "node_modules/socks": {
784 | "version": "2.8.4",
785 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
786 | "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
787 | "license": "MIT",
788 | "dependencies": {
789 | "ip-address": "^9.0.5",
790 | "smart-buffer": "^4.2.0"
791 | },
792 | "engines": {
793 | "node": ">= 10.0.0",
794 | "npm": ">= 3.0.0"
795 | }
796 | },
797 | "node_modules/sparse-bitfield": {
798 | "version": "3.0.3",
799 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
800 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
801 | "license": "MIT",
802 | "optional": true,
803 | "dependencies": {
804 | "memory-pager": "^1.0.2"
805 | }
806 | },
807 | "node_modules/sprintf-js": {
808 | "version": "1.1.3",
809 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
810 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
811 | "license": "BSD-3-Clause"
812 | },
813 | "node_modules/string-width": {
814 | "version": "5.1.2",
815 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
816 | "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
817 | "license": "MIT",
818 | "dependencies": {
819 | "eastasianwidth": "^0.2.0",
820 | "emoji-regex": "^9.2.2",
821 | "strip-ansi": "^7.0.1"
822 | },
823 | "engines": {
824 | "node": ">=12"
825 | },
826 | "funding": {
827 | "url": "https://github.com/sponsors/sindresorhus"
828 | }
829 | },
830 | "node_modules/string-width-cjs": {
831 | "name": "string-width",
832 | "version": "4.2.3",
833 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
834 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
835 | "license": "MIT",
836 | "dependencies": {
837 | "emoji-regex": "^8.0.0",
838 | "is-fullwidth-code-point": "^3.0.0",
839 | "strip-ansi": "^6.0.1"
840 | },
841 | "engines": {
842 | "node": ">=8"
843 | }
844 | },
845 | "node_modules/string-width-cjs/node_modules/ansi-regex": {
846 | "version": "5.0.1",
847 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
848 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
849 | "license": "MIT",
850 | "engines": {
851 | "node": ">=8"
852 | }
853 | },
854 | "node_modules/string-width-cjs/node_modules/emoji-regex": {
855 | "version": "8.0.0",
856 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
857 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
858 | "license": "MIT"
859 | },
860 | "node_modules/string-width-cjs/node_modules/strip-ansi": {
861 | "version": "6.0.1",
862 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
863 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
864 | "license": "MIT",
865 | "dependencies": {
866 | "ansi-regex": "^5.0.1"
867 | },
868 | "engines": {
869 | "node": ">=8"
870 | }
871 | },
872 | "node_modules/strip-ansi": {
873 | "version": "7.1.0",
874 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
875 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
876 | "license": "MIT",
877 | "dependencies": {
878 | "ansi-regex": "^6.0.1"
879 | },
880 | "engines": {
881 | "node": ">=12"
882 | },
883 | "funding": {
884 | "url": "https://github.com/chalk/strip-ansi?sponsor=1"
885 | }
886 | },
887 | "node_modules/strip-ansi-cjs": {
888 | "name": "strip-ansi",
889 | "version": "6.0.1",
890 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
891 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
892 | "license": "MIT",
893 | "dependencies": {
894 | "ansi-regex": "^5.0.1"
895 | },
896 | "engines": {
897 | "node": ">=8"
898 | }
899 | },
900 | "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
901 | "version": "5.0.1",
902 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
903 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
904 | "license": "MIT",
905 | "engines": {
906 | "node": ">=8"
907 | }
908 | },
909 | "node_modules/tr46": {
910 | "version": "3.0.0",
911 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
912 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
913 | "license": "MIT",
914 | "dependencies": {
915 | "punycode": "^2.1.1"
916 | },
917 | "engines": {
918 | "node": ">=12"
919 | }
920 | },
921 | "node_modules/ts-mixer": {
922 | "version": "6.0.4",
923 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
924 | "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==",
925 | "license": "MIT"
926 | },
927 | "node_modules/tslib": {
928 | "version": "2.8.1",
929 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
930 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
931 | "license": "0BSD"
932 | },
933 | "node_modules/undici": {
934 | "version": "6.21.1",
935 | "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz",
936 | "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==",
937 | "license": "MIT",
938 | "engines": {
939 | "node": ">=18.17"
940 | }
941 | },
942 | "node_modules/undici-types": {
943 | "version": "6.21.0",
944 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
945 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
946 | "license": "MIT"
947 | },
948 | "node_modules/webidl-conversions": {
949 | "version": "7.0.0",
950 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
951 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
952 | "license": "BSD-2-Clause",
953 | "engines": {
954 | "node": ">=12"
955 | }
956 | },
957 | "node_modules/whatwg-url": {
958 | "version": "11.0.0",
959 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
960 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
961 | "license": "MIT",
962 | "dependencies": {
963 | "tr46": "^3.0.0",
964 | "webidl-conversions": "^7.0.0"
965 | },
966 | "engines": {
967 | "node": ">=12"
968 | }
969 | },
970 | "node_modules/which": {
971 | "version": "2.0.2",
972 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
973 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
974 | "license": "ISC",
975 | "dependencies": {
976 | "isexe": "^2.0.0"
977 | },
978 | "bin": {
979 | "node-which": "bin/node-which"
980 | },
981 | "engines": {
982 | "node": ">= 8"
983 | }
984 | },
985 | "node_modules/wrap-ansi": {
986 | "version": "8.1.0",
987 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
988 | "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
989 | "license": "MIT",
990 | "dependencies": {
991 | "ansi-styles": "^6.1.0",
992 | "string-width": "^5.0.1",
993 | "strip-ansi": "^7.0.1"
994 | },
995 | "engines": {
996 | "node": ">=12"
997 | },
998 | "funding": {
999 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
1000 | }
1001 | },
1002 | "node_modules/wrap-ansi-cjs": {
1003 | "name": "wrap-ansi",
1004 | "version": "7.0.0",
1005 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
1006 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
1007 | "license": "MIT",
1008 | "dependencies": {
1009 | "ansi-styles": "^4.0.0",
1010 | "string-width": "^4.1.0",
1011 | "strip-ansi": "^6.0.0"
1012 | },
1013 | "engines": {
1014 | "node": ">=10"
1015 | },
1016 | "funding": {
1017 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
1018 | }
1019 | },
1020 | "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
1021 | "version": "5.0.1",
1022 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
1023 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
1024 | "license": "MIT",
1025 | "engines": {
1026 | "node": ">=8"
1027 | }
1028 | },
1029 | "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
1030 | "version": "4.3.0",
1031 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
1032 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
1033 | "license": "MIT",
1034 | "dependencies": {
1035 | "color-convert": "^2.0.1"
1036 | },
1037 | "engines": {
1038 | "node": ">=8"
1039 | },
1040 | "funding": {
1041 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
1042 | }
1043 | },
1044 | "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
1045 | "version": "8.0.0",
1046 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
1047 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
1048 | "license": "MIT"
1049 | },
1050 | "node_modules/wrap-ansi-cjs/node_modules/string-width": {
1051 | "version": "4.2.3",
1052 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
1053 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1054 | "license": "MIT",
1055 | "dependencies": {
1056 | "emoji-regex": "^8.0.0",
1057 | "is-fullwidth-code-point": "^3.0.0",
1058 | "strip-ansi": "^6.0.1"
1059 | },
1060 | "engines": {
1061 | "node": ">=8"
1062 | }
1063 | },
1064 | "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
1065 | "version": "6.0.1",
1066 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1067 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1068 | "license": "MIT",
1069 | "dependencies": {
1070 | "ansi-regex": "^5.0.1"
1071 | },
1072 | "engines": {
1073 | "node": ">=8"
1074 | }
1075 | },
1076 | "node_modules/ws": {
1077 | "version": "8.18.1",
1078 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
1079 | "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
1080 | "license": "MIT",
1081 | "engines": {
1082 | "node": ">=10.0.0"
1083 | },
1084 | "peerDependencies": {
1085 | "bufferutil": "^4.0.1",
1086 | "utf-8-validate": ">=5.0.2"
1087 | },
1088 | "peerDependenciesMeta": {
1089 | "bufferutil": {
1090 | "optional": true
1091 | },
1092 | "utf-8-validate": {
1093 | "optional": true
1094 | }
1095 | }
1096 | }
1097 | }
1098 | }
1099 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "automod-bot",
3 | "version": "1.0.0",
4 | "description": "A powerful Discord moderation bot with auto mod, slash commands, and a dashboard",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "deploy": "node deploy-commands.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/Khanmanan/automod-bot.git"
13 | },
14 | "author": "cwkhan",
15 | "license": "MIT",
16 | "dependencies": {
17 | "discord.js": "^14.13.0",
18 | "dotenv": "^16.3.1",
19 | "mongoose": "^7.6.1",
20 | "moment": "^2.29.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/toggle-antispam.js:
--------------------------------------------------------------------------------
1 | const GuildSettings = require('../models/GuildSettings');
2 |
3 | module.exports = {
4 | name: 'messageDelete',
5 | async execute(message) {
6 | if (message.partial || message.author?.bot || !message.guild) return;
7 |
8 | const settings = await GuildSettings.findOne({ guildId: message.guild.id });
9 | if (!settings?.automodEnabled || !settings?.antiGhostPing) return;
10 |
11 | if (message.mentions.users.size > 0) {
12 | await message.channel.send({
13 | content: `👻 **Ghost ping detected!**\nUser: <@${message.author.id}>`,
14 | });
15 | }
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/utils/logToChannel.js:
--------------------------------------------------------------------------------
1 | // utils/logToChannel.js
2 | const { EmbedBuilder } = require('discord.js');
3 | const GuildSettings = require('../models/GuildSettings');
4 |
5 | module.exports = async function logToChannel(guild, embedData) {
6 | const settings = await GuildSettings.findOne({ guildId: guild.id });
7 | if (!settings?.logChannelId) return;
8 |
9 | const logChannel = guild.channels.cache.get(settings.logChannelId);
10 | if (!logChannel) return;
11 |
12 | const embed = new EmbedBuilder()
13 | .setTitle(embedData.title)
14 | .addFields(...embedData.fields)
15 | .setColor(embedData.color || 'Blue')
16 | .setTimestamp();
17 |
18 | logChannel.send({ embeds: [embed] });
19 | };
20 |
--------------------------------------------------------------------------------