├── .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 | ![Automod Banner](https://cwkbot.fun/botbanner.png) 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 | --------------------------------------------------------------------------------