├── .gitignore ├── 01 - Setting up project ├── .env.example ├── package.json └── src │ └── index.js ├── 02 - Registering Slash Commands ├── .env.example ├── package.json └── src │ ├── index.js │ └── register-commands.js ├── 03 - Options and Choices ├── .env.example ├── package.json └── src │ ├── index.js │ └── register-commands.js ├── 04 - Embeds ├── .env.example ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── register-commands.js ├── 05 - Button Roles ├── .env.example ├── package-lock.json ├── package.json └── src │ ├── index.js │ ├── register-commands.js │ └── send-message.js ├── 06 - Custom Status ├── .env.example ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── register-commands.js ├── 07 - Command Handler ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── misc │ │ └── ping.js │ └── moderation │ │ └── ban.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ └── utils │ ├── areCommandsDifferent.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 08 - Ping Command ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── misc │ │ └── ping.js │ └── moderation │ │ └── ban.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ └── utils │ ├── areCommandsDifferent.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 09 - Kick and Ban Command ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ └── kick.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ └── utils │ ├── areCommandsDifferent.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 10 - Timeout Command ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ └── utils │ ├── areCommandsDifferent.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 11 - Connect to DB ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ └── utils │ ├── areCommandsDifferent.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 12 - Levelling system ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ ├── messageCreate │ │ └── giveUserXp.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ ├── models │ └── Level.js │ └── utils │ ├── areCommandsDifferent.js │ ├── calculateLevelXp.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 13 - Level command ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── economy │ │ └── level.js │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ ├── messageCreate │ │ └── giveUserXp.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ ├── models │ └── Level.js │ └── utils │ ├── areCommandsDifferent.js │ ├── calculateLevelXp.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 14 - Daily Command ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── economy │ │ ├── daily.js │ │ └── level.js │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── interactionCreate │ │ └── handleCommands.js │ ├── messageCreate │ │ └── giveUserXp.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ ├── models │ ├── Level.js │ └── User.js │ └── utils │ ├── areCommandsDifferent.js │ ├── calculateLevelXp.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 15 - AutoRole ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── admin │ │ ├── autorole-configure.js │ │ └── autorole-disable.js │ ├── economy │ │ ├── daily.js │ │ └── level.js │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── guildMemberAdd │ │ └── autoRole.js │ ├── interactionCreate │ │ └── handleCommands.js │ ├── messageCreate │ │ └── giveUserXp.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ ├── models │ ├── AutoRole.js │ ├── Level.js │ └── User.js │ └── utils │ ├── areCommandsDifferent.js │ ├── calculateLevelXp.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── 16 - Balance Command ├── .env.example ├── config.json ├── package-lock.json ├── package.json └── src │ ├── commands │ ├── admin │ │ ├── autorole-configure.js │ │ └── autorole-disable.js │ ├── economy │ │ ├── balance.js │ │ ├── daily.js │ │ └── level.js │ ├── misc │ │ └── ping.js │ └── moderation │ │ ├── ban.js │ │ ├── kick.js │ │ └── timeout.js │ ├── events │ ├── guildMemberAdd │ │ └── autoRole.js │ ├── interactionCreate │ │ └── handleCommands.js │ ├── messageCreate │ │ └── giveUserXp.js │ └── ready │ │ ├── 01registerCommands.js │ │ └── consoleLog.js │ ├── handlers │ └── eventHandler.js │ ├── index.js │ ├── models │ ├── AutoRole.js │ ├── Level.js │ └── User.js │ └── utils │ ├── areCommandsDifferent.js │ ├── calculateLevelXp.js │ ├── getAllFiles.js │ ├── getApplicationCommands.js │ └── getLocalCommands.js ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .DS_Store -------------------------------------------------------------------------------- /01 - Setting up project/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN -------------------------------------------------------------------------------- /01 - Setting up project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /01 - Setting up project/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | 4 | const client = new Client({ 5 | intents: [ 6 | IntentsBitField.Flags.Guilds, 7 | IntentsBitField.Flags.GuildMembers, 8 | IntentsBitField.Flags.GuildMessages, 9 | IntentsBitField.Flags.MessageContent, 10 | ], 11 | }); 12 | 13 | client.on('ready', (c) => { 14 | console.log(`✅ ${c.user.tag} is online.`); 15 | }); 16 | 17 | client.on('messageCreate', (message) => { 18 | if (message.author.bot) { 19 | return; 20 | } 21 | 22 | if (message.content === 'hello') { 23 | message.reply('hello'); 24 | } 25 | }); 26 | 27 | client.login(process.env.TOKEN); 28 | -------------------------------------------------------------------------------- /02 - Registering Slash Commands/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /02 - Registering Slash Commands/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /02 - Registering Slash Commands/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | 4 | const client = new Client({ 5 | intents: [ 6 | IntentsBitField.Flags.Guilds, 7 | IntentsBitField.Flags.GuildMembers, 8 | IntentsBitField.Flags.GuildMessages, 9 | IntentsBitField.Flags.MessageContent, 10 | ], 11 | }); 12 | 13 | client.on('ready', (c) => { 14 | console.log(`✅ ${c.user.tag} is online.`); 15 | }); 16 | 17 | client.on('interactionCreate', (interaction) => { 18 | if (!interaction.isChatInputCommand()) return; 19 | 20 | if (interaction.commandName === 'hey') { 21 | return interaction.reply('hey!'); 22 | } 23 | 24 | if (interaction.commandName === 'ping') { 25 | return interaction.reply('Pong!'); 26 | } 27 | }); 28 | 29 | client.login(process.env.TOKEN); 30 | -------------------------------------------------------------------------------- /02 - Registering Slash Commands/src/register-commands.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { REST, Routes } = require('discord.js'); 3 | 4 | const commands = [ 5 | { 6 | name: 'hey', 7 | description: 'Replies with hey!', 8 | }, 9 | { 10 | name: 'ping', 11 | description: 'Pong!', 12 | }, 13 | ]; 14 | 15 | const rest = new REST({ version: '10' }).setToken(process.env.TOKEN); 16 | 17 | (async () => { 18 | try { 19 | console.log('Registering slash commands...'); 20 | 21 | await rest.put( 22 | Routes.applicationGuildCommands( 23 | process.env.CLIENT_ID, 24 | process.env.GUILD_ID 25 | ), 26 | { body: commands } 27 | ); 28 | 29 | console.log('Slash commands were registered successfully!'); 30 | } catch (error) { 31 | console.log(`There was an error: ${error}`); 32 | } 33 | })(); 34 | -------------------------------------------------------------------------------- /03 - Options and Choices/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /03 - Options and Choices/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /03 - Options and Choices/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | 4 | const client = new Client({ 5 | intents: [ 6 | IntentsBitField.Flags.Guilds, 7 | IntentsBitField.Flags.GuildMembers, 8 | IntentsBitField.Flags.GuildMessages, 9 | IntentsBitField.Flags.MessageContent, 10 | ], 11 | }); 12 | 13 | client.on('ready', (c) => { 14 | console.log(`✅ ${c.user.tag} is online.`); 15 | }); 16 | 17 | client.on('interactionCreate', (interaction) => { 18 | if (!interaction.isChatInputCommand()) return; 19 | 20 | if (interaction.commandName === 'add') { 21 | const num1 = interaction.options.get('first-number').value; 22 | const num2 = interaction.options.get('second-number').value; 23 | 24 | interaction.reply(`The sum is ${num1 + num2}`); 25 | } 26 | }); 27 | 28 | client.login(process.env.TOKEN); 29 | -------------------------------------------------------------------------------- /03 - Options and Choices/src/register-commands.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { REST, Routes, ApplicationCommandOptionType } = require('discord.js'); 3 | 4 | const commands = [ 5 | { 6 | name: 'add', 7 | description: 'Adds two numbers.', 8 | options: [ 9 | { 10 | name: 'first-number', 11 | description: 'The first number.', 12 | type: ApplicationCommandOptionType.String, 13 | choices: [ 14 | { 15 | name: 'one', 16 | value: '1', 17 | }, 18 | { 19 | name: 'two', 20 | value: '2', 21 | }, 22 | { 23 | name: 'three', 24 | value: '3', 25 | }, 26 | ], 27 | required: true, 28 | }, 29 | { 30 | name: 'second-number', 31 | description: 'The second number.', 32 | type: ApplicationCommandOptionType.Number, 33 | required: true, 34 | }, 35 | ], 36 | }, 37 | ]; 38 | 39 | const rest = new REST({ version: '10' }).setToken(process.env.TOKEN); 40 | 41 | (async () => { 42 | try { 43 | console.log('Registering slash commands...'); 44 | 45 | await rest.put( 46 | Routes.applicationGuildCommands( 47 | process.env.CLIENT_ID, 48 | process.env.GUILD_ID 49 | ), 50 | { body: commands } 51 | ); 52 | 53 | console.log('Slash commands were registered successfully!'); 54 | } catch (error) { 55 | console.log(`There was an error: ${error}`); 56 | } 57 | })(); 58 | -------------------------------------------------------------------------------- /04 - Embeds/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /04 - Embeds/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /04 - Embeds/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField, EmbedBuilder } = require('discord.js'); 3 | 4 | const client = new Client({ 5 | intents: [ 6 | IntentsBitField.Flags.Guilds, 7 | IntentsBitField.Flags.GuildMembers, 8 | IntentsBitField.Flags.GuildMessages, 9 | IntentsBitField.Flags.MessageContent, 10 | ], 11 | }); 12 | 13 | client.on('ready', (c) => { 14 | console.log(`✅ ${c.user.tag} is online.`); 15 | }); 16 | 17 | client.on('interactionCreate', (interaction) => { 18 | if (!interaction.isChatInputCommand()) return; 19 | 20 | if (interaction.commandName === 'embed') { 21 | const embed = new EmbedBuilder() 22 | .setTitle('Embed title') 23 | .setDescription('This is an embed description') 24 | .setColor('Random') 25 | .addFields( 26 | { 27 | name: 'Field title', 28 | value: 'Some random value', 29 | inline: true, 30 | }, 31 | { 32 | name: '2nd Field title', 33 | value: 'Some random value', 34 | inline: true, 35 | } 36 | ); 37 | 38 | interaction.reply({ embeds: [embed] }); 39 | } 40 | }); 41 | 42 | client.on('messageCreate', (message) => { 43 | if (message.content === 'embed') { 44 | const embed = new EmbedBuilder() 45 | .setTitle('Embed title') 46 | .setDescription('This is an embed description') 47 | .setColor('Random') 48 | .addFields( 49 | { 50 | name: 'Field title', 51 | value: 'Some random value', 52 | inline: true, 53 | }, 54 | { 55 | name: '2nd Field title', 56 | value: 'Some random value', 57 | inline: true, 58 | } 59 | ); 60 | 61 | message.channel.send({ embeds: [embed] }); 62 | } 63 | }); 64 | 65 | client.login(process.env.TOKEN); 66 | -------------------------------------------------------------------------------- /04 - Embeds/src/register-commands.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { REST, Routes, ApplicationCommandOptionType } = require('discord.js'); 3 | 4 | const commands = [ 5 | { 6 | name: 'embed', 7 | description: 'Sends an embed!', 8 | }, 9 | ]; 10 | 11 | const rest = new REST({ version: '10' }).setToken(process.env.TOKEN); 12 | 13 | (async () => { 14 | try { 15 | console.log('Registering slash commands...'); 16 | 17 | await rest.put( 18 | Routes.applicationGuildCommands( 19 | process.env.CLIENT_ID, 20 | process.env.GUILD_ID 21 | ), 22 | { body: commands } 23 | ); 24 | 25 | console.log('Slash commands were registered successfully!'); 26 | } catch (error) { 27 | console.log(`There was an error: ${error}`); 28 | } 29 | })(); 30 | -------------------------------------------------------------------------------- /05 - Button Roles/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /05 - Button Roles/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /05 - Button Roles/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | 4 | const client = new Client({ 5 | intents: [ 6 | IntentsBitField.Flags.Guilds, 7 | IntentsBitField.Flags.GuildMembers, 8 | IntentsBitField.Flags.GuildMessages, 9 | IntentsBitField.Flags.MessageContent, 10 | ], 11 | }); 12 | 13 | client.on('ready', (c) => { 14 | console.log(`✅ ${c.user.tag} is online.`); 15 | }); 16 | 17 | client.on('interactionCreate', async (interaction) => { 18 | try { 19 | if (!interaction.isButton()) return; 20 | await interaction.deferReply({ ephemeral: true }); 21 | 22 | const role = interaction.guild.roles.cache.get(interaction.customId); 23 | if (!role) { 24 | interaction.editReply({ 25 | content: "I couldn't find that role", 26 | }); 27 | return; 28 | } 29 | 30 | const hasRole = interaction.member.roles.cache.has(role.id); 31 | 32 | if (hasRole) { 33 | await interaction.member.roles.remove(role); 34 | await interaction.editReply(`The role ${role} has been removed.`); 35 | return; 36 | } 37 | 38 | await interaction.member.roles.add(role); 39 | await interaction.editReply(`The role ${role} has been added.`); 40 | } catch (error) { 41 | console.log(error); 42 | } 43 | }); 44 | 45 | client.login(process.env.TOKEN); 46 | -------------------------------------------------------------------------------- /05 - Button Roles/src/register-commands.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { REST, Routes, ApplicationCommandOptionType } = require('discord.js'); 3 | 4 | const commands = []; 5 | 6 | const rest = new REST({ version: '10' }).setToken(process.env.TOKEN); 7 | 8 | (async () => { 9 | try { 10 | console.log('Registering slash commands...'); 11 | 12 | await rest.put( 13 | Routes.applicationGuildCommands( 14 | process.env.CLIENT_ID, 15 | process.env.GUILD_ID 16 | ), 17 | { body: commands } 18 | ); 19 | 20 | console.log('Slash commands were registered successfully!'); 21 | } catch (error) { 22 | console.log(`There was an error: ${error}`); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /05 - Button Roles/src/send-message.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { 3 | Client, 4 | IntentsBitField, 5 | ActionRowBuilder, 6 | ButtonBuilder, 7 | ButtonStyle, 8 | } = require('discord.js'); 9 | 10 | const client = new Client({ 11 | intents: [ 12 | IntentsBitField.Flags.Guilds, 13 | IntentsBitField.Flags.GuildMembers, 14 | IntentsBitField.Flags.GuildMessages, 15 | IntentsBitField.Flags.MessageContent, 16 | ], 17 | }); 18 | 19 | const roles = [ 20 | { 21 | id: '1058030952707784714', 22 | label: 'Red', 23 | }, 24 | { 25 | id: '1058031008655609856', 26 | label: 'Green', 27 | }, 28 | { 29 | id: '1058031040461013093', 30 | label: 'Blue', 31 | }, 32 | ]; 33 | 34 | client.on('ready', async (c) => { 35 | try { 36 | const channel = await client.channels.cache.get('1049345076188422226'); 37 | if (!channel) return; 38 | 39 | const row = new ActionRowBuilder(); 40 | 41 | roles.forEach((role) => { 42 | row.components.push( 43 | new ButtonBuilder() 44 | .setCustomId(role.id) 45 | .setLabel(role.label) 46 | .setStyle(ButtonStyle.Primary) 47 | ); 48 | }); 49 | 50 | await channel.send({ 51 | content: 'Claim or remove a role below.', 52 | components: [row], 53 | }); 54 | process.exit(); 55 | } catch (error) { 56 | console.log(error); 57 | } 58 | }); 59 | 60 | client.login(process.env.TOKEN); 61 | -------------------------------------------------------------------------------- /06 - Custom Status/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /06 - Custom Status/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /06 - Custom Status/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField, ActivityType } = require('discord.js'); 3 | 4 | const client = new Client({ 5 | intents: [ 6 | IntentsBitField.Flags.Guilds, 7 | IntentsBitField.Flags.GuildMembers, 8 | IntentsBitField.Flags.GuildMessages, 9 | IntentsBitField.Flags.MessageContent, 10 | ], 11 | }); 12 | 13 | let status = [ 14 | { 15 | name: 'Under Ctrl', 16 | type: ActivityType.Streaming, 17 | url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', 18 | }, 19 | { 20 | name: 'Custom Status 1', 21 | }, 22 | { 23 | name: 'Custom Status 2', 24 | type: ActivityType.Watching, 25 | }, 26 | { 27 | name: 'Custom Status 3', 28 | type: ActivityType.Listening, 29 | }, 30 | ]; 31 | 32 | client.on('ready', (c) => { 33 | console.log(`✅ ${c.user.tag} is online.`); 34 | 35 | setInterval(() => { 36 | let random = Math.floor(Math.random() * status.length); 37 | client.user.setActivity(status[random]); 38 | }, 10000); 39 | }); 40 | 41 | client.login(process.env.TOKEN); 42 | -------------------------------------------------------------------------------- /06 - Custom Status/src/register-commands.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { REST, Routes, ApplicationCommandOptionType } = require('discord.js'); 3 | 4 | const commands = []; 5 | 6 | const rest = new REST({ version: '10' }).setToken(process.env.TOKEN); 7 | 8 | (async () => { 9 | try { 10 | console.log('Registering slash commands...'); 11 | 12 | await rest.put( 13 | Routes.applicationGuildCommands( 14 | process.env.CLIENT_ID, 15 | process.env.GUILD_ID 16 | ), 17 | { body: commands } 18 | ); 19 | 20 | console.log('Slash commands were registered successfully!'); 21 | } catch (error) { 22 | console.log(`There was an error: ${error}`); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /07 - Command Handler/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /07 - Command Handler/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /07 - Command Handler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /07 - Command Handler/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Pong!', 4 | // devOnly: Boolean, 5 | testOnly: true, 6 | // options: Object[], 7 | // deleted: Boolean, 8 | 9 | callback: (client, interaction) => { 10 | interaction.reply(`Pong! ${client.ws.ping}ms`); 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /07 - Command Handler/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | ApplicationCommandOptionType, 3 | PermissionFlagsBits, 4 | } = require('discord.js'); 5 | 6 | module.exports = { 7 | deleted: true, 8 | name: 'ban', 9 | description: 'Bans a member!!!', 10 | // devOnly: Boolean, 11 | // testOnly: Boolean, 12 | options: [ 13 | { 14 | name: 'target-user', 15 | description: 'The user to ban.', 16 | required: true, 17 | type: ApplicationCommandOptionType.Mentionable, 18 | }, 19 | { 20 | name: 'reason', 21 | description: 'The reason for banning.', 22 | type: ApplicationCommandOptionType.String, 23 | }, 24 | ], 25 | permissionsRequired: [PermissionFlagsBits.Administrator], 26 | botPermissions: [PermissionFlagsBits.Administrator], 27 | 28 | callback: (client, interaction) => { 29 | interaction.reply('ban..'); 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /07 - Command Handler/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /07 - Command Handler/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`TThere was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /07 - Command Handler/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /07 - Command Handler/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | const eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort((a, b) => a > b); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /07 - Command Handler/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const eventHandler = require('./handlers/eventHandler'); 4 | 5 | const client = new Client({ 6 | intents: [ 7 | IntentsBitField.Flags.Guilds, 8 | IntentsBitField.Flags.GuildMembers, 9 | IntentsBitField.Flags.GuildMessages, 10 | IntentsBitField.Flags.MessageContent, 11 | ], 12 | }); 13 | 14 | eventHandler(client); 15 | 16 | client.login(process.env.TOKEN); 17 | -------------------------------------------------------------------------------- /07 - Command Handler/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /07 - Command Handler/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /07 - Command Handler/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /07 - Command Handler/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /08 - Ping Command/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /08 - Ping Command/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /08 - Ping Command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /08 - Ping Command/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /08 - Ping Command/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | ApplicationCommandOptionType, 3 | PermissionFlagsBits, 4 | } = require('discord.js'); 5 | 6 | module.exports = { 7 | deleted: true, 8 | name: 'ban', 9 | description: 'Bans a member!!!', 10 | // devOnly: Boolean, 11 | // testOnly: Boolean, 12 | options: [ 13 | { 14 | name: 'target-user', 15 | description: 'The user to ban.', 16 | required: true, 17 | type: ApplicationCommandOptionType.Mentionable, 18 | }, 19 | { 20 | name: 'reason', 21 | description: 'The reason for banning.', 22 | type: ApplicationCommandOptionType.String, 23 | }, 24 | ], 25 | permissionsRequired: [PermissionFlagsBits.Administrator], 26 | botPermissions: [PermissionFlagsBits.Administrator], 27 | 28 | callback: (client, interaction) => { 29 | interaction.reply('ban..'); 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /08 - Ping Command/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /08 - Ping Command/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`TThere was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /08 - Ping Command/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /08 - Ping Command/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | const eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort((a, b) => a > b); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /08 - Ping Command/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const eventHandler = require('./handlers/eventHandler'); 4 | 5 | const client = new Client({ 6 | intents: [ 7 | IntentsBitField.Flags.Guilds, 8 | IntentsBitField.Flags.GuildMembers, 9 | IntentsBitField.Flags.GuildMessages, 10 | IntentsBitField.Flags.MessageContent, 11 | ], 12 | }); 13 | 14 | eventHandler(client); 15 | 16 | client.login(process.env.TOKEN); 17 | -------------------------------------------------------------------------------- /08 - Ping Command/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /08 - Ping Command/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /08 - Ping Command/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /08 - Ping Command/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | CLIENT_ID = YOUR_BOT_ID 3 | GUILD_ID = YOUR_SERVER_ID -------------------------------------------------------------------------------- /09 - Kick and Ban Command/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | const eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort((a, b) => a > b); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const eventHandler = require('./handlers/eventHandler'); 4 | 5 | const client = new Client({ 6 | intents: [ 7 | IntentsBitField.Flags.Guilds, 8 | IntentsBitField.Flags.GuildMembers, 9 | IntentsBitField.Flags.GuildMessages, 10 | IntentsBitField.Flags.MessageContent, 11 | ], 12 | }); 13 | 14 | eventHandler(client); 15 | 16 | client.login(process.env.TOKEN); 17 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /09 - Kick and Ban Command/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /10 - Timeout Command/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN -------------------------------------------------------------------------------- /10 - Timeout Command/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /10 - Timeout Command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3", 15 | "ms": "^2.1.3", 16 | "pretty-ms": "^8.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles = eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const eventHandler = require('./handlers/eventHandler'); 4 | 5 | const client = new Client({ 6 | intents: [ 7 | IntentsBitField.Flags.Guilds, 8 | IntentsBitField.Flags.GuildMembers, 9 | IntentsBitField.Flags.GuildMessages, 10 | IntentsBitField.Flags.MessageContent, 11 | ], 12 | }); 13 | 14 | eventHandler(client); 15 | 16 | client.login(process.env.TOKEN); 17 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /10 - Timeout Command/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /11 - Connect to DB/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | MONGODB_URI = YOUR_MONGO_URI -------------------------------------------------------------------------------- /11 - Connect to DB/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /11 - Connect to DB/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3", 15 | "mongoose": "^6.9.0", 16 | "ms": "^2.1.3", 17 | "pretty-ms": "^8.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles = eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const mongoose = require('mongoose'); 4 | const eventHandler = require('./handlers/eventHandler'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.MessageContent, 12 | ], 13 | }); 14 | 15 | (async () => { 16 | try { 17 | mongoose.set('strictQuery', false); 18 | await mongoose.connect(process.env.MONGODB_URI, { keepAlive: true }); 19 | console.log('Connected to DB.'); 20 | 21 | eventHandler(client); 22 | } catch (error) { 23 | console.log(`Error: ${error}`); 24 | } 25 | })(); 26 | 27 | client.login(process.env.TOKEN); 28 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /11 - Connect to DB/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /12 - Levelling system/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | MONGODB_URI = YOUR_MONGO_URI -------------------------------------------------------------------------------- /12 - Levelling system/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /12 - Levelling system/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "discord.js": "^14.7.1", 14 | "dotenv": "^16.0.3", 15 | "mongoose": "^6.9.0", 16 | "ms": "^2.1.3", 17 | "pretty-ms": "^8.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /12 - Levelling system/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /12 - Levelling system/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /12 - Levelling system/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /12 - Levelling system/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /12 - Levelling system/src/events/messageCreate/giveUserXp.js: -------------------------------------------------------------------------------- 1 | const { Client, Message } = require('discord.js'); 2 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 3 | const Level = require('../../models/Level'); 4 | const cooldowns = new Set(); 5 | 6 | function getRandomXp(min, max) { 7 | min = Math.ceil(min); 8 | max = Math.floor(max); 9 | return Math.floor(Math.random() * (max - min + 1)) + min; 10 | } 11 | 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Message} message 16 | */ 17 | module.exports = async (client, message) => { 18 | if (!message.inGuild() || message.author.bot || cooldowns.has(message.author.id)) return; 19 | 20 | const xpToGive = getRandomXp(5, 15); 21 | 22 | const query = { 23 | userId: message.author.id, 24 | guildId: message.guild.id, 25 | }; 26 | 27 | try { 28 | const level = await Level.findOne(query); 29 | 30 | if (level) { 31 | level.xp += xpToGive; 32 | 33 | if (level.xp > calculateLevelXp(level.level)) { 34 | level.xp = 0; 35 | level.level += 1; 36 | 37 | message.channel.send(`${message.member} you have leveled up to **level ${level.level}**.`); 38 | } 39 | 40 | await level.save().catch((e) => { 41 | console.log(`Error saving updated level ${e}`); 42 | return; 43 | }); 44 | cooldowns.add(message.author.id); 45 | setTimeout(() => { 46 | cooldowns.delete(message.author.id); 47 | }, 60000); 48 | } 49 | 50 | // if (!level) 51 | else { 52 | // create new level 53 | const newLevel = new Level({ 54 | userId: message.author.id, 55 | guildId: message.guild.id, 56 | xp: xpToGive, 57 | }); 58 | 59 | await newLevel.save(); 60 | cooldowns.add(message.author.id); 61 | setTimeout(() => { 62 | cooldowns.delete(message.author.id); 63 | }, 60000); 64 | } 65 | } catch (error) { 66 | console.log(`Error giving xp: ${error}`); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /12 - Levelling system/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /12 - Levelling system/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /12 - Levelling system/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /12 - Levelling system/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const mongoose = require('mongoose'); 4 | const eventHandler = require('./handlers/eventHandler'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.MessageContent, 12 | ], 13 | }); 14 | 15 | (async () => { 16 | try { 17 | mongoose.set('strictQuery', false); 18 | await mongoose.connect(process.env.MONGODB_URI); 19 | console.log('Connected to DB.'); 20 | 21 | eventHandler(client); 22 | 23 | client.login(process.env.TOKEN); 24 | } catch (error) { 25 | console.log(`Error: ${error}`); 26 | } 27 | })(); 28 | -------------------------------------------------------------------------------- /12 - Levelling system/src/models/Level.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const levelSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | xp: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | level: { 17 | type: Number, 18 | default: 0, 19 | }, 20 | }); 21 | 22 | module.exports = model('Level', levelSchema); 23 | -------------------------------------------------------------------------------- /12 - Levelling system/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /12 - Levelling system/src/utils/calculateLevelXp.js: -------------------------------------------------------------------------------- 1 | module.exports = (level) => 100 * level || 1; 2 | -------------------------------------------------------------------------------- /12 - Levelling system/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /12 - Levelling system/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /12 - Levelling system/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /13 - Level command/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | MONGODB_URI = YOUR_MONGO_URI -------------------------------------------------------------------------------- /13 - Level command/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /13 - Level command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "canvacord": "^5.4.8", 14 | "discord.js": "^14.7.1", 15 | "dotenv": "^16.0.3", 16 | "mongoose": "^6.9.0", 17 | "ms": "^2.1.3", 18 | "pretty-ms": "^8.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /13 - Level command/src/commands/economy/level.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | AttachmentBuilder, 6 | } = require('discord.js'); 7 | const canvacord = require('canvacord'); 8 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 9 | const Level = require('../../models/Level'); 10 | 11 | module.exports = { 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Interaction} interaction 16 | */ 17 | callback: async (client, interaction) => { 18 | if (!interaction.inGuild()) { 19 | interaction.reply('You can only run this command inside a server.'); 20 | return; 21 | } 22 | 23 | await interaction.deferReply(); 24 | 25 | const mentionedUserId = interaction.options.get('target-user')?.value; 26 | const targetUserId = mentionedUserId || interaction.member.id; 27 | const targetUserObj = await interaction.guild.members.fetch(targetUserId); 28 | 29 | const fetchedLevel = await Level.findOne({ 30 | userId: targetUserId, 31 | guildId: interaction.guild.id, 32 | }); 33 | 34 | if (!fetchedLevel) { 35 | interaction.editReply( 36 | mentionedUserId 37 | ? `${targetUserObj.user.tag} doesn't have any levels yet. Try again when they chat a little more.` 38 | : "You don't have any levels yet. Chat a little more and try again." 39 | ); 40 | return; 41 | } 42 | 43 | let allLevels = await Level.find({ guildId: interaction.guild.id }).select( 44 | '-_id userId level xp' 45 | ); 46 | 47 | allLevels.sort((a, b) => { 48 | if (a.level === b.level) { 49 | return b.xp - a.xp; 50 | } else { 51 | return b.level - a.level; 52 | } 53 | }); 54 | 55 | let currentRank = allLevels.findIndex((lvl) => lvl.userId === targetUserId) + 1; 56 | 57 | const rank = new canvacord.Rank() 58 | .setAvatar(targetUserObj.user.displayAvatarURL({ size: 256 })) 59 | .setRank(currentRank) 60 | .setLevel(fetchedLevel.level) 61 | .setCurrentXP(fetchedLevel.xp) 62 | .setRequiredXP(calculateLevelXp(fetchedLevel.level)) 63 | .setStatus(targetUserObj.presence.status) 64 | .setProgressBar('#FFC300', 'COLOR') 65 | .setUsername(targetUserObj.user.username) 66 | .setDiscriminator(targetUserObj.user.discriminator); 67 | 68 | const data = await rank.build(); 69 | const attachment = new AttachmentBuilder(data); 70 | interaction.editReply({ files: [attachment] }); 71 | }, 72 | 73 | name: 'level', 74 | description: "Shows your/someone's level.", 75 | options: [ 76 | { 77 | name: 'target-user', 78 | description: 'The user whose level you want to see.', 79 | type: ApplicationCommandOptionType.Mentionable, 80 | }, 81 | ], 82 | }; 83 | -------------------------------------------------------------------------------- /13 - Level command/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /13 - Level command/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /13 - Level command/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /13 - Level command/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /13 - Level command/src/events/messageCreate/giveUserXp.js: -------------------------------------------------------------------------------- 1 | const { Client, Message } = require('discord.js'); 2 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 3 | const Level = require('../../models/Level'); 4 | const cooldowns = new Set(); 5 | 6 | function getRandomXp(min, max) { 7 | min = Math.ceil(min); 8 | max = Math.floor(max); 9 | return Math.floor(Math.random() * (max - min + 1)) + min; 10 | } 11 | 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Message} message 16 | */ 17 | module.exports = async (client, message) => { 18 | if (!message.inGuild() || message.author.bot || cooldowns.has(message.author.id)) return; 19 | 20 | const xpToGive = getRandomXp(5, 15); 21 | 22 | const query = { 23 | userId: message.author.id, 24 | guildId: message.guild.id, 25 | }; 26 | 27 | try { 28 | const level = await Level.findOne(query); 29 | 30 | if (level) { 31 | level.xp += xpToGive; 32 | 33 | if (level.xp > calculateLevelXp(level.level)) { 34 | level.xp = 0; 35 | level.level += 1; 36 | 37 | message.channel.send(`${message.member} you have leveled up to **level ${level.level}**.`); 38 | } 39 | 40 | await level.save().catch((e) => { 41 | console.log(`Error saving updated level ${e}`); 42 | return; 43 | }); 44 | cooldowns.add(message.author.id); 45 | setTimeout(() => { 46 | cooldowns.delete(message.author.id); 47 | }, 60000); 48 | } 49 | 50 | // if (!level) 51 | else { 52 | // create new level 53 | const newLevel = new Level({ 54 | userId: message.author.id, 55 | guildId: message.guild.id, 56 | xp: xpToGive, 57 | }); 58 | 59 | await newLevel.save(); 60 | cooldowns.add(message.author.id); 61 | setTimeout(() => { 62 | cooldowns.delete(message.author.id); 63 | }, 60000); 64 | } 65 | } catch (error) { 66 | console.log(`Error giving xp: ${error}`); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /13 - Level command/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /13 - Level command/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /13 - Level command/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /13 - Level command/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const mongoose = require('mongoose'); 4 | const eventHandler = require('./handlers/eventHandler'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.GuildPresences, 12 | IntentsBitField.Flags.MessageContent, 13 | ], 14 | }); 15 | 16 | (async () => { 17 | try { 18 | mongoose.set('strictQuery', false); 19 | await mongoose.connect(process.env.MONGODB_URI); 20 | console.log('Connected to DB.'); 21 | 22 | eventHandler(client); 23 | 24 | client.login(process.env.TOKEN); 25 | } catch (error) { 26 | console.log(`Error: ${error}`); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /13 - Level command/src/models/Level.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const levelSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | xp: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | level: { 17 | type: Number, 18 | default: 0, 19 | }, 20 | }); 21 | 22 | module.exports = model('Level', levelSchema); 23 | -------------------------------------------------------------------------------- /13 - Level command/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /13 - Level command/src/utils/calculateLevelXp.js: -------------------------------------------------------------------------------- 1 | module.exports = (level) => 100 * level || 1; 2 | -------------------------------------------------------------------------------- /13 - Level command/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /13 - Level command/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /13 - Level command/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /14 - Daily Command/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | MONGODB_URI = YOUR_MONGO_URI -------------------------------------------------------------------------------- /14 - Daily Command/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /14 - Daily Command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "canvacord": "^5.4.8", 14 | "discord.js": "^14.7.1", 15 | "dotenv": "^16.0.3", 16 | "mongoose": "^6.9.0", 17 | "ms": "^2.1.3", 18 | "pretty-ms": "^8.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /14 - Daily Command/src/commands/economy/daily.js: -------------------------------------------------------------------------------- 1 | const { Client, Interaction } = require('discord.js'); 2 | const User = require('../../models/User'); 3 | 4 | const dailyAmount = 1000; 5 | 6 | module.exports = { 7 | name: 'daily', 8 | description: 'Collect your dailies!', 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | callback: async (client, interaction) => { 15 | if (!interaction.inGuild()) { 16 | interaction.reply({ 17 | content: 'You can only run this command inside a server.', 18 | ephemeral: true, 19 | }); 20 | return; 21 | } 22 | 23 | try { 24 | await interaction.deferReply(); 25 | 26 | const query = { 27 | userId: interaction.member.id, 28 | guildId: interaction.guild.id, 29 | }; 30 | 31 | let user = await User.findOne(query); 32 | 33 | if (user) { 34 | const lastDailyDate = user.lastDaily.toDateString(); 35 | const currentDate = new Date().toDateString(); 36 | 37 | if (lastDailyDate === currentDate) { 38 | interaction.editReply( 39 | 'You have already collected your dailies today. Come back tomorrow!' 40 | ); 41 | return; 42 | } 43 | 44 | user.lastDaily = new Date(); 45 | } else { 46 | user = new User({ 47 | ...query, 48 | lastDaily: new Date(), 49 | }); 50 | } 51 | 52 | user.balance += dailyAmount; 53 | await user.save(); 54 | 55 | interaction.editReply( 56 | `${dailyAmount} was added to your balance. Your new balance is ${user.balance}` 57 | ); 58 | } catch (error) { 59 | console.log(`Error with /daily: ${error}`); 60 | } 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /14 - Daily Command/src/commands/economy/level.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | AttachmentBuilder, 6 | } = require('discord.js'); 7 | const canvacord = require('canvacord'); 8 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 9 | const Level = require('../../models/Level'); 10 | 11 | module.exports = { 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Interaction} interaction 16 | */ 17 | callback: async (client, interaction) => { 18 | if (!interaction.inGuild()) { 19 | interaction.reply('You can only run this command inside a server.'); 20 | return; 21 | } 22 | 23 | await interaction.deferReply(); 24 | 25 | const mentionedUserId = interaction.options.get('target-user')?.value; 26 | const targetUserId = mentionedUserId || interaction.member.id; 27 | const targetUserObj = await interaction.guild.members.fetch(targetUserId); 28 | 29 | const fetchedLevel = await Level.findOne({ 30 | userId: targetUserId, 31 | guildId: interaction.guild.id, 32 | }); 33 | 34 | if (!fetchedLevel) { 35 | interaction.editReply( 36 | mentionedUserId 37 | ? `${targetUserObj.user.tag} doesn't have any levels yet. Try again when they chat a little more.` 38 | : "You don't have any levels yet. Chat a little more and try again." 39 | ); 40 | return; 41 | } 42 | 43 | let allLevels = await Level.find({ guildId: interaction.guild.id }).select( 44 | '-_id userId level xp' 45 | ); 46 | 47 | allLevels.sort((a, b) => { 48 | if (a.level === b.level) { 49 | return b.xp - a.xp; 50 | } else { 51 | return b.level - a.level; 52 | } 53 | }); 54 | 55 | let currentRank = allLevels.findIndex((lvl) => lvl.userId === targetUserId) + 1; 56 | 57 | const rank = new canvacord.Rank() 58 | .setAvatar(targetUserObj.user.displayAvatarURL({ size: 256 })) 59 | .setRank(currentRank) 60 | .setLevel(fetchedLevel.level) 61 | .setCurrentXP(fetchedLevel.xp) 62 | .setRequiredXP(calculateLevelXp(fetchedLevel.level)) 63 | .setStatus(targetUserObj.presence.status) 64 | .setProgressBar('#FFC300', 'COLOR') 65 | .setUsername(targetUserObj.user.username) 66 | .setDiscriminator(targetUserObj.user.discriminator); 67 | 68 | const data = await rank.build(); 69 | const attachment = new AttachmentBuilder(data); 70 | interaction.editReply({ files: [attachment] }); 71 | }, 72 | 73 | name: 'level', 74 | description: "Shows your/someone's level.", 75 | options: [ 76 | { 77 | name: 'target-user', 78 | description: 'The user whose level you want to see.', 79 | type: ApplicationCommandOptionType.Mentionable, 80 | }, 81 | ], 82 | }; 83 | -------------------------------------------------------------------------------- /14 - Daily Command/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /14 - Daily Command/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /14 - Daily Command/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /14 - Daily Command/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /14 - Daily Command/src/events/messageCreate/giveUserXp.js: -------------------------------------------------------------------------------- 1 | const { Client, Message } = require('discord.js'); 2 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 3 | const Level = require('../../models/Level'); 4 | const cooldowns = new Set(); 5 | 6 | function getRandomXp(min, max) { 7 | min = Math.ceil(min); 8 | max = Math.floor(max); 9 | return Math.floor(Math.random() * (max - min + 1)) + min; 10 | } 11 | 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Message} message 16 | */ 17 | module.exports = async (client, message) => { 18 | if (!message.inGuild() || message.author.bot || cooldowns.has(message.author.id)) return; 19 | 20 | const xpToGive = getRandomXp(5, 15); 21 | 22 | const query = { 23 | userId: message.author.id, 24 | guildId: message.guild.id, 25 | }; 26 | 27 | try { 28 | const level = await Level.findOne(query); 29 | 30 | if (level) { 31 | level.xp += xpToGive; 32 | 33 | if (level.xp > calculateLevelXp(level.level)) { 34 | level.xp = 0; 35 | level.level += 1; 36 | 37 | message.channel.send(`${message.member} you have leveled up to **level ${level.level}**.`); 38 | } 39 | 40 | await level.save().catch((e) => { 41 | console.log(`Error saving updated level ${e}`); 42 | return; 43 | }); 44 | cooldowns.add(message.author.id); 45 | setTimeout(() => { 46 | cooldowns.delete(message.author.id); 47 | }, 60000); 48 | } 49 | 50 | // if (!level) 51 | else { 52 | // create new level 53 | const newLevel = new Level({ 54 | userId: message.author.id, 55 | guildId: message.guild.id, 56 | xp: xpToGive, 57 | }); 58 | 59 | await newLevel.save(); 60 | cooldowns.add(message.author.id); 61 | setTimeout(() => { 62 | cooldowns.delete(message.author.id); 63 | }, 60000); 64 | } 65 | } catch (error) { 66 | console.log(`Error giving xp: ${error}`); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /14 - Daily Command/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /14 - Daily Command/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /14 - Daily Command/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /14 - Daily Command/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const mongoose = require('mongoose'); 4 | const eventHandler = require('./handlers/eventHandler'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.GuildPresences, 12 | IntentsBitField.Flags.MessageContent, 13 | ], 14 | }); 15 | 16 | (async () => { 17 | try { 18 | mongoose.set('strictQuery', false); 19 | await mongoose.connect(process.env.MONGODB_URI); 20 | console.log('Connected to DB.'); 21 | 22 | eventHandler(client); 23 | 24 | client.login(process.env.TOKEN); 25 | } catch (error) { 26 | console.log(`Error: ${error}`); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /14 - Daily Command/src/models/Level.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const levelSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | xp: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | level: { 17 | type: Number, 18 | default: 0, 19 | }, 20 | }); 21 | 22 | module.exports = model('Level', levelSchema); 23 | -------------------------------------------------------------------------------- /14 - Daily Command/src/models/User.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const userSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | balance: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | lastDaily: { 17 | type: Date, 18 | reqired: true, 19 | }, 20 | }); 21 | 22 | module.exports = model('User', userSchema); 23 | -------------------------------------------------------------------------------- /14 - Daily Command/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /14 - Daily Command/src/utils/calculateLevelXp.js: -------------------------------------------------------------------------------- 1 | module.exports = (level) => 100 * level || 1; 2 | -------------------------------------------------------------------------------- /14 - Daily Command/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /14 - Daily Command/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /14 - Daily Command/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /15 - AutoRole/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | MONGODB_URI = YOUR_MONGO_URI -------------------------------------------------------------------------------- /15 - AutoRole/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /15 - AutoRole/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "canvacord": "^5.4.8", 14 | "discord.js": "^14.7.1", 15 | "dotenv": "^16.0.3", 16 | "mongoose": "^6.9.0", 17 | "ms": "^2.1.3", 18 | "pretty-ms": "^8.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/admin/autorole-configure.js: -------------------------------------------------------------------------------- 1 | const { ApplicationCommandOptionType, Client, Interaction, PermissionFlagsBits } = require('discord.js'); 2 | const AutoRole = require('../../models/AutoRole'); 3 | 4 | module.exports = { 5 | /** 6 | * 7 | * @param {Client} client 8 | * @param {Interaction} interaction 9 | */ 10 | callback: async (client, interaction) => { 11 | if (!interaction.inGuild()) { 12 | interaction.reply('You can only run this command inside a server.'); 13 | return; 14 | } 15 | 16 | const targetRoleId = interaction.options.get('role').value; 17 | 18 | try { 19 | await interaction.deferReply(); 20 | 21 | let autoRole = await AutoRole.findOne({ guildId: interaction.guild.id }); 22 | 23 | if (autoRole) { 24 | if (autoRole.roleId === targetRoleId) { 25 | interaction.editReply('Auto role has already been configured for that role. To disable run `/autorole-disable`'); 26 | return; 27 | } 28 | 29 | autoRole.roleId = targetRoleId; 30 | } else { 31 | autoRole = new AutoRole({ 32 | guildId: interaction.guild.id, 33 | roleId: targetRoleId, 34 | }); 35 | } 36 | 37 | await autoRole.save(); 38 | interaction.editReply('Autorole has now been configured. To disable run `/autorole-disable`'); 39 | } catch (error) { 40 | console.log(error); 41 | } 42 | }, 43 | 44 | name: 'autorole-configure', 45 | description: 'Configure your auto-role for this server.', 46 | options: [ 47 | { 48 | name: 'role', 49 | description: 'The role you want users to get on join.', 50 | type: ApplicationCommandOptionType.Role, 51 | required: true, 52 | }, 53 | ], 54 | permissionsRequired: [PermissionFlagsBits.Administrator], 55 | botPermissions: [PermissionFlagsBits.ManageRoles], 56 | }; 57 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/admin/autorole-disable.js: -------------------------------------------------------------------------------- 1 | const { Client, Interaction, PermissionFlagsBits } = require('discord.js'); 2 | const AutoRole = require('../../models/AutoRole'); 3 | 4 | module.exports = { 5 | /** 6 | * 7 | * @param {Client} client 8 | * @param {Interaction} interaction 9 | */ 10 | callback: async (client, interaction) => { 11 | try { 12 | await interaction.deferReply(); 13 | 14 | if (!(await AutoRole.exists({ guildId: interaction.guild.id }))) { 15 | interaction.editReply('Auto role has not been configured for this server. Use `/autorole-configure` to set it up.'); 16 | return; 17 | } 18 | 19 | await AutoRole.findOneAndDelete({ guildId: interaction.guild.id }); 20 | interaction.editReply('Auto role has been disabled for this server. Use `/autorole-configure` to set it up again.'); 21 | } catch (error) { 22 | console.log(error); 23 | } 24 | }, 25 | 26 | name: 'autorole-disable', 27 | description: 'Disable auto-role in this server.', 28 | permissionsRequired: [PermissionFlagsBits.Administrator], 29 | }; 30 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/economy/daily.js: -------------------------------------------------------------------------------- 1 | const { Client, Interaction } = require('discord.js'); 2 | const User = require('../../models/User'); 3 | 4 | const dailyAmount = 1000; 5 | 6 | module.exports = { 7 | name: 'daily', 8 | description: 'Collect your dailies!', 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | callback: async (client, interaction) => { 15 | if (!interaction.inGuild()) { 16 | interaction.reply({ 17 | content: 'You can only run this command inside a server.', 18 | ephemeral: true, 19 | }); 20 | return; 21 | } 22 | 23 | try { 24 | await interaction.deferReply(); 25 | 26 | const query = { 27 | userId: interaction.member.id, 28 | guildId: interaction.guild.id, 29 | }; 30 | 31 | let user = await User.findOne(query); 32 | 33 | if (user) { 34 | const lastDailyDate = user.lastDaily.toDateString(); 35 | const currentDate = new Date().toDateString(); 36 | 37 | if (lastDailyDate === currentDate) { 38 | interaction.editReply( 39 | 'You have already collected your dailies today. Come back tomorrow!' 40 | ); 41 | return; 42 | } 43 | 44 | user.lastDaily = new Date(); 45 | } else { 46 | user = new User({ 47 | ...query, 48 | lastDaily: new Date(), 49 | }); 50 | } 51 | 52 | user.balance += dailyAmount; 53 | await user.save(); 54 | 55 | interaction.editReply( 56 | `${dailyAmount} was added to your balance. Your new balance is ${user.balance}` 57 | ); 58 | } catch (error) { 59 | console.log(`Error with /daily: ${error}`); 60 | } 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/economy/level.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | AttachmentBuilder, 6 | } = require('discord.js'); 7 | const canvacord = require('canvacord'); 8 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 9 | const Level = require('../../models/Level'); 10 | 11 | module.exports = { 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Interaction} interaction 16 | */ 17 | callback: async (client, interaction) => { 18 | if (!interaction.inGuild()) { 19 | interaction.reply('You can only run this command inside a server.'); 20 | return; 21 | } 22 | 23 | await interaction.deferReply(); 24 | 25 | const mentionedUserId = interaction.options.get('target-user')?.value; 26 | const targetUserId = mentionedUserId || interaction.member.id; 27 | const targetUserObj = await interaction.guild.members.fetch(targetUserId); 28 | 29 | const fetchedLevel = await Level.findOne({ 30 | userId: targetUserId, 31 | guildId: interaction.guild.id, 32 | }); 33 | 34 | if (!fetchedLevel) { 35 | interaction.editReply( 36 | mentionedUserId 37 | ? `${targetUserObj.user.tag} doesn't have any levels yet. Try again when they chat a little more.` 38 | : "You don't have any levels yet. Chat a little more and try again." 39 | ); 40 | return; 41 | } 42 | 43 | let allLevels = await Level.find({ guildId: interaction.guild.id }).select( 44 | '-_id userId level xp' 45 | ); 46 | 47 | allLevels.sort((a, b) => { 48 | if (a.level === b.level) { 49 | return b.xp - a.xp; 50 | } else { 51 | return b.level - a.level; 52 | } 53 | }); 54 | 55 | let currentRank = allLevels.findIndex((lvl) => lvl.userId === targetUserId) + 1; 56 | 57 | const rank = new canvacord.Rank() 58 | .setAvatar(targetUserObj.user.displayAvatarURL({ size: 256 })) 59 | .setRank(currentRank) 60 | .setLevel(fetchedLevel.level) 61 | .setCurrentXP(fetchedLevel.xp) 62 | .setRequiredXP(calculateLevelXp(fetchedLevel.level)) 63 | .setStatus(targetUserObj.presence.status) 64 | .setProgressBar('#FFC300', 'COLOR') 65 | .setUsername(targetUserObj.user.username) 66 | .setDiscriminator(targetUserObj.user.discriminator); 67 | 68 | const data = await rank.build(); 69 | const attachment = new AttachmentBuilder(data); 70 | interaction.editReply({ files: [attachment] }); 71 | }, 72 | 73 | name: 'level', 74 | description: "Shows your/someone's level.", 75 | options: [ 76 | { 77 | name: 'target-user', 78 | description: 'The user whose level you want to see.', 79 | type: ApplicationCommandOptionType.Mentionable, 80 | }, 81 | ], 82 | }; 83 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /15 - AutoRole/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /15 - AutoRole/src/events/guildMemberAdd/autoRole.js: -------------------------------------------------------------------------------- 1 | const { Client, GuildMember } = require('discord.js'); 2 | const AutoRole = require('../../models/AutoRole'); 3 | 4 | /** 5 | * 6 | * @param {Client} client 7 | * @param {GuildMember} member 8 | */ 9 | module.exports = async (client, member) => { 10 | try { 11 | let guild = member.guild; 12 | if (!guild) return; 13 | 14 | const autoRole = await AutoRole.findOne({ guildId: guild.id }); 15 | if (!autoRole) return; 16 | 17 | await member.roles.add(autoRole.roleId); 18 | } catch (error) { 19 | console.log(`Error giving role automatically: ${error}`); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /15 - AutoRole/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /15 - AutoRole/src/events/messageCreate/giveUserXp.js: -------------------------------------------------------------------------------- 1 | const { Client, Message } = require('discord.js'); 2 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 3 | const Level = require('../../models/Level'); 4 | const cooldowns = new Set(); 5 | 6 | function getRandomXp(min, max) { 7 | min = Math.ceil(min); 8 | max = Math.floor(max); 9 | return Math.floor(Math.random() * (max - min + 1)) + min; 10 | } 11 | 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Message} message 16 | */ 17 | module.exports = async (client, message) => { 18 | if (!message.inGuild() || message.author.bot || cooldowns.has(message.author.id)) return; 19 | 20 | const xpToGive = getRandomXp(5, 15); 21 | 22 | const query = { 23 | userId: message.author.id, 24 | guildId: message.guild.id, 25 | }; 26 | 27 | try { 28 | const level = await Level.findOne(query); 29 | 30 | if (level) { 31 | level.xp += xpToGive; 32 | 33 | if (level.xp > calculateLevelXp(level.level)) { 34 | level.xp = 0; 35 | level.level += 1; 36 | 37 | message.channel.send(`${message.member} you have leveled up to **level ${level.level}**.`); 38 | } 39 | 40 | await level.save().catch((e) => { 41 | console.log(`Error saving updated level ${e}`); 42 | return; 43 | }); 44 | cooldowns.add(message.author.id); 45 | setTimeout(() => { 46 | cooldowns.delete(message.author.id); 47 | }, 60000); 48 | } 49 | 50 | // if (!level) 51 | else { 52 | // create new level 53 | const newLevel = new Level({ 54 | userId: message.author.id, 55 | guildId: message.guild.id, 56 | xp: xpToGive, 57 | }); 58 | 59 | await newLevel.save(); 60 | cooldowns.add(message.author.id); 61 | setTimeout(() => { 62 | cooldowns.delete(message.author.id); 63 | }, 60000); 64 | } 65 | } catch (error) { 66 | console.log(`Error giving xp: ${error}`); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /15 - AutoRole/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /15 - AutoRole/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /15 - AutoRole/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /15 - AutoRole/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const mongoose = require('mongoose'); 4 | const eventHandler = require('./handlers/eventHandler'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.GuildPresences, 12 | IntentsBitField.Flags.MessageContent, 13 | ], 14 | }); 15 | 16 | (async () => { 17 | try { 18 | mongoose.set('strictQuery', false); 19 | await mongoose.connect(process.env.MONGODB_URI); 20 | console.log('Connected to DB.'); 21 | 22 | eventHandler(client); 23 | 24 | client.login(process.env.TOKEN); 25 | } catch (error) { 26 | console.log(`Error: ${error}`); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /15 - AutoRole/src/models/AutoRole.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const autoRoleSchema = new Schema({ 4 | guildId: { 5 | type: String, 6 | required: true, 7 | unique: true, 8 | }, 9 | roleId: { 10 | type: String, 11 | required: true, 12 | }, 13 | }); 14 | 15 | module.exports = model('AutoRole', autoRoleSchema); 16 | -------------------------------------------------------------------------------- /15 - AutoRole/src/models/Level.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const levelSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | xp: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | level: { 17 | type: Number, 18 | default: 0, 19 | }, 20 | }); 21 | 22 | module.exports = model('Level', levelSchema); 23 | -------------------------------------------------------------------------------- /15 - AutoRole/src/models/User.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const userSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | balance: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | lastDaily: { 17 | type: Date, 18 | reqired: true, 19 | }, 20 | }); 21 | 22 | module.exports = model('User', userSchema); 23 | -------------------------------------------------------------------------------- /15 - AutoRole/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /15 - AutoRole/src/utils/calculateLevelXp.js: -------------------------------------------------------------------------------- 1 | module.exports = (level) => 100 * level || 1; 2 | -------------------------------------------------------------------------------- /15 - AutoRole/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /15 - AutoRole/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /15 - AutoRole/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /16 - Balance Command/.env.example: -------------------------------------------------------------------------------- 1 | TOKEN = YOUR_BOT_TOKEN 2 | MONGODB_URI = YOUR_MONGO_URI -------------------------------------------------------------------------------- /16 - Balance Command/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testServer": "1049345075366334617", 3 | "clientId": "1051228977068519565", 4 | "devs": ["1049343381903515778"] 5 | } 6 | -------------------------------------------------------------------------------- /16 - Balance Command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "canvacord": "^5.4.8", 14 | "discord.js": "^14.7.1", 15 | "dotenv": "^16.0.3", 16 | "mongoose": "^6.9.0", 17 | "ms": "^2.1.3", 18 | "pretty-ms": "^8.0.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/admin/autorole-configure.js: -------------------------------------------------------------------------------- 1 | const { ApplicationCommandOptionType, Client, Interaction, PermissionFlagsBits } = require('discord.js'); 2 | const AutoRole = require('../../models/AutoRole'); 3 | 4 | module.exports = { 5 | /** 6 | * 7 | * @param {Client} client 8 | * @param {Interaction} interaction 9 | */ 10 | callback: async (client, interaction) => { 11 | if (!interaction.inGuild()) { 12 | interaction.reply('You can only run this command inside a server.'); 13 | return; 14 | } 15 | 16 | const targetRoleId = interaction.options.get('role').value; 17 | 18 | try { 19 | await interaction.deferReply(); 20 | 21 | let autoRole = await AutoRole.findOne({ guildId: interaction.guild.id }); 22 | 23 | if (autoRole) { 24 | if (autoRole.roleId === targetRoleId) { 25 | interaction.editReply('Auto role has already been configured for that role. To disable run `/autorole-disable`'); 26 | return; 27 | } 28 | 29 | autoRole.roleId = targetRoleId; 30 | } else { 31 | autoRole = new AutoRole({ 32 | guildId: interaction.guild.id, 33 | roleId: targetRoleId, 34 | }); 35 | } 36 | 37 | await autoRole.save(); 38 | interaction.editReply('Autorole has now been configured. To disable run `/autorole-disable`'); 39 | } catch (error) { 40 | console.log(error); 41 | } 42 | }, 43 | 44 | name: 'autorole-configure', 45 | description: 'Configure your auto-role for this server.', 46 | options: [ 47 | { 48 | name: 'role', 49 | description: 'The role you want users to get on join.', 50 | type: ApplicationCommandOptionType.Role, 51 | required: true, 52 | }, 53 | ], 54 | permissionsRequired: [PermissionFlagsBits.Administrator], 55 | botPermissions: [PermissionFlagsBits.ManageRoles], 56 | }; 57 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/admin/autorole-disable.js: -------------------------------------------------------------------------------- 1 | const { Client, Interaction, PermissionFlagsBits } = require('discord.js'); 2 | const AutoRole = require('../../models/AutoRole'); 3 | 4 | module.exports = { 5 | /** 6 | * 7 | * @param {Client} client 8 | * @param {Interaction} interaction 9 | */ 10 | callback: async (client, interaction) => { 11 | try { 12 | await interaction.deferReply(); 13 | 14 | if (!(await AutoRole.exists({ guildId: interaction.guild.id }))) { 15 | interaction.editReply('Auto role has not been configured for this server. Use `/autorole-configure` to set it up.'); 16 | return; 17 | } 18 | 19 | await AutoRole.findOneAndDelete({ guildId: interaction.guild.id }); 20 | interaction.editReply('Auto role has been disabled for this server. Use `/autorole-configure` to set it up again.'); 21 | } catch (error) { 22 | console.log(error); 23 | } 24 | }, 25 | 26 | name: 'autorole-disable', 27 | description: 'Disable auto-role in this server.', 28 | permissionsRequired: [PermissionFlagsBits.Administrator], 29 | }; 30 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/economy/balance.js: -------------------------------------------------------------------------------- 1 | const { Client, Interaction, ApplicationCommandOptionType } = require('discord.js'); 2 | const User = require('../../models/User'); 3 | 4 | module.exports = { 5 | /** 6 | * 7 | * @param {Client} client 8 | * @param {Interaction} interaction 9 | */ 10 | callback: async (client, interaction) => { 11 | if (!interaction.inGuild()) { 12 | interaction.reply({ 13 | content: 'You can only run this command inside a server.', 14 | ephemeral: true, 15 | }); 16 | return; 17 | } 18 | 19 | const targetUserId = interaction.options.get('user')?.value || interaction.member.id; 20 | 21 | await interaction.deferReply(); 22 | 23 | const user = await User.findOne({ userId: targetUserId, guildId: interaction.guild.id }); 24 | 25 | if (!user) { 26 | interaction.editReply(`<@${targetUserId}> doesn't have a profile yet.`); 27 | return; 28 | } 29 | 30 | interaction.editReply( 31 | targetUserId === interaction.member.id 32 | ? `Your balance is **${user.balance}**` 33 | : `<@${targetUserId}>'s balance is **${user.balance}**` 34 | ); 35 | }, 36 | 37 | name: 'balance', 38 | description: "See yours/someone else's balance", 39 | options: [ 40 | { 41 | name: 'user', 42 | description: 'The user whose balance you want to get.', 43 | type: ApplicationCommandOptionType.User, 44 | }, 45 | ], 46 | }; 47 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/economy/daily.js: -------------------------------------------------------------------------------- 1 | const { Client, Interaction } = require('discord.js'); 2 | const User = require('../../models/User'); 3 | 4 | const dailyAmount = 1000; 5 | 6 | module.exports = { 7 | name: 'daily', 8 | description: 'Collect your dailies!', 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | callback: async (client, interaction) => { 15 | if (!interaction.inGuild()) { 16 | interaction.reply({ 17 | content: 'You can only run this command inside a server.', 18 | ephemeral: true, 19 | }); 20 | return; 21 | } 22 | 23 | try { 24 | await interaction.deferReply(); 25 | 26 | const query = { 27 | userId: interaction.member.id, 28 | guildId: interaction.guild.id, 29 | }; 30 | 31 | let user = await User.findOne(query); 32 | 33 | if (user) { 34 | const lastDailyDate = user.lastDaily.toDateString(); 35 | const currentDate = new Date().toDateString(); 36 | 37 | if (lastDailyDate === currentDate) { 38 | interaction.editReply( 39 | 'You have already collected your dailies today. Come back tomorrow!' 40 | ); 41 | return; 42 | } 43 | 44 | user.lastDaily = new Date(); 45 | } else { 46 | user = new User({ 47 | ...query, 48 | lastDaily: new Date(), 49 | }); 50 | } 51 | 52 | user.balance += dailyAmount; 53 | await user.save(); 54 | 55 | interaction.editReply( 56 | `${dailyAmount} was added to your balance. Your new balance is ${user.balance}` 57 | ); 58 | } catch (error) { 59 | console.log(`Error with /daily: ${error}`); 60 | } 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/misc/ping.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'ping', 3 | description: 'Replies with the bot ping!', 4 | 5 | callback: async (client, interaction) => { 6 | await interaction.deferReply(); 7 | 8 | const reply = await interaction.fetchReply(); 9 | 10 | const ping = reply.createdTimestamp - interaction.createdTimestamp; 11 | 12 | interaction.editReply( 13 | `Pong! Client ${ping}ms | Websocket: ${client.ws.ping}ms` 14 | ); 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/moderation/ban.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't ban that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't ban that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't ban that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Ban the targetUser 55 | try { 56 | await targetUser.ban({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was banned\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when banning: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'ban', 66 | description: 'Bans a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to ban.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to ban.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.BanMembers], 81 | botPermissions: [PermissionFlagsBits.BanMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /16 - Balance Command/src/commands/moderation/kick.js: -------------------------------------------------------------------------------- 1 | const { 2 | Client, 3 | Interaction, 4 | ApplicationCommandOptionType, 5 | PermissionFlagsBits, 6 | } = require('discord.js'); 7 | 8 | module.exports = { 9 | /** 10 | * 11 | * @param {Client} client 12 | * @param {Interaction} interaction 13 | */ 14 | 15 | callback: async (client, interaction) => { 16 | const targetUserId = interaction.options.get('target-user').value; 17 | const reason = 18 | interaction.options.get('reason')?.value || 'No reason provided'; 19 | 20 | await interaction.deferReply(); 21 | 22 | const targetUser = await interaction.guild.members.fetch(targetUserId); 23 | 24 | if (!targetUser) { 25 | await interaction.editReply("That user doesn't exist in this server."); 26 | return; 27 | } 28 | 29 | if (targetUser.id === interaction.guild.ownerId) { 30 | await interaction.editReply( 31 | "You can't kick that user because they're the server owner." 32 | ); 33 | return; 34 | } 35 | 36 | const targetUserRolePosition = targetUser.roles.highest.position; // Highest role of the target user 37 | const requestUserRolePosition = interaction.member.roles.highest.position; // Highest role of the user running the cmd 38 | const botRolePosition = interaction.guild.members.me.roles.highest.position; // Highest role of the bot 39 | 40 | if (targetUserRolePosition >= requestUserRolePosition) { 41 | await interaction.editReply( 42 | "You can't kick that user because they have the same/higher role than you." 43 | ); 44 | return; 45 | } 46 | 47 | if (targetUserRolePosition >= botRolePosition) { 48 | await interaction.editReply( 49 | "I can't kick that user because they have the same/higher role than me." 50 | ); 51 | return; 52 | } 53 | 54 | // Kick the targetUser 55 | try { 56 | await targetUser.kick({ reason }); 57 | await interaction.editReply( 58 | `User ${targetUser} was kicked\nReason: ${reason}` 59 | ); 60 | } catch (error) { 61 | console.log(`There was an error when kicking: ${error}`); 62 | } 63 | }, 64 | 65 | name: 'kick', 66 | description: 'Kicks a member from this server.', 67 | options: [ 68 | { 69 | name: 'target-user', 70 | description: 'The user you want to kick.', 71 | type: ApplicationCommandOptionType.Mentionable, 72 | required: true, 73 | }, 74 | { 75 | name: 'reason', 76 | description: 'The reason you want to kick.', 77 | type: ApplicationCommandOptionType.String, 78 | }, 79 | ], 80 | permissionsRequired: [PermissionFlagsBits.KickMembers], 81 | botPermissions: [PermissionFlagsBits.KickMembers], 82 | }; 83 | -------------------------------------------------------------------------------- /16 - Balance Command/src/events/guildMemberAdd/autoRole.js: -------------------------------------------------------------------------------- 1 | const { Client, GuildMember } = require('discord.js'); 2 | const AutoRole = require('../../models/AutoRole'); 3 | 4 | /** 5 | * 6 | * @param {Client} client 7 | * @param {GuildMember} member 8 | */ 9 | module.exports = async (client, member) => { 10 | try { 11 | let guild = member.guild; 12 | if (!guild) return; 13 | 14 | const autoRole = await AutoRole.findOne({ guildId: guild.id }); 15 | if (!autoRole) return; 16 | 17 | await member.roles.add(autoRole.roleId); 18 | } catch (error) { 19 | console.log(`Error giving role automatically: ${error}`); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /16 - Balance Command/src/events/interactionCreate/handleCommands.js: -------------------------------------------------------------------------------- 1 | const { devs, testServer } = require('../../../config.json'); 2 | const getLocalCommands = require('../../utils/getLocalCommands'); 3 | 4 | module.exports = async (client, interaction) => { 5 | if (!interaction.isChatInputCommand()) return; 6 | 7 | const localCommands = getLocalCommands(); 8 | 9 | try { 10 | const commandObject = localCommands.find( 11 | (cmd) => cmd.name === interaction.commandName 12 | ); 13 | 14 | if (!commandObject) return; 15 | 16 | if (commandObject.devOnly) { 17 | if (!devs.includes(interaction.member.id)) { 18 | interaction.reply({ 19 | content: 'Only developers are allowed to run this command.', 20 | ephemeral: true, 21 | }); 22 | return; 23 | } 24 | } 25 | 26 | if (commandObject.testOnly) { 27 | if (!(interaction.guild.id === testServer)) { 28 | interaction.reply({ 29 | content: 'This command cannot be ran here.', 30 | ephemeral: true, 31 | }); 32 | return; 33 | } 34 | } 35 | 36 | if (commandObject.permissionsRequired?.length) { 37 | for (const permission of commandObject.permissionsRequired) { 38 | if (!interaction.member.permissions.has(permission)) { 39 | interaction.reply({ 40 | content: 'Not enough permissions.', 41 | ephemeral: true, 42 | }); 43 | return; 44 | } 45 | } 46 | } 47 | 48 | if (commandObject.botPermissions?.length) { 49 | for (const permission of commandObject.botPermissions) { 50 | const bot = interaction.guild.members.me; 51 | 52 | if (!bot.permissions.has(permission)) { 53 | interaction.reply({ 54 | content: "I don't have enough permissions.", 55 | ephemeral: true, 56 | }); 57 | return; 58 | } 59 | } 60 | } 61 | 62 | await commandObject.callback(client, interaction); 63 | } catch (error) { 64 | console.log(`There was an error running this command: ${error}`); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /16 - Balance Command/src/events/messageCreate/giveUserXp.js: -------------------------------------------------------------------------------- 1 | const { Client, Message } = require('discord.js'); 2 | const calculateLevelXp = require('../../utils/calculateLevelXp'); 3 | const Level = require('../../models/Level'); 4 | const cooldowns = new Set(); 5 | 6 | function getRandomXp(min, max) { 7 | min = Math.ceil(min); 8 | max = Math.floor(max); 9 | return Math.floor(Math.random() * (max - min + 1)) + min; 10 | } 11 | 12 | /** 13 | * 14 | * @param {Client} client 15 | * @param {Message} message 16 | */ 17 | module.exports = async (client, message) => { 18 | if (!message.inGuild() || message.author.bot || cooldowns.has(message.author.id)) return; 19 | 20 | const xpToGive = getRandomXp(5, 15); 21 | 22 | const query = { 23 | userId: message.author.id, 24 | guildId: message.guild.id, 25 | }; 26 | 27 | try { 28 | const level = await Level.findOne(query); 29 | 30 | if (level) { 31 | level.xp += xpToGive; 32 | 33 | if (level.xp > calculateLevelXp(level.level)) { 34 | level.xp = 0; 35 | level.level += 1; 36 | 37 | message.channel.send(`${message.member} you have leveled up to **level ${level.level}**.`); 38 | } 39 | 40 | await level.save().catch((e) => { 41 | console.log(`Error saving updated level ${e}`); 42 | return; 43 | }); 44 | cooldowns.add(message.author.id); 45 | setTimeout(() => { 46 | cooldowns.delete(message.author.id); 47 | }, 60000); 48 | } 49 | 50 | // if (!level) 51 | else { 52 | // create new level 53 | const newLevel = new Level({ 54 | userId: message.author.id, 55 | guildId: message.guild.id, 56 | xp: xpToGive, 57 | }); 58 | 59 | await newLevel.save(); 60 | cooldowns.add(message.author.id); 61 | setTimeout(() => { 62 | cooldowns.delete(message.author.id); 63 | }, 60000); 64 | } 65 | } catch (error) { 66 | console.log(`Error giving xp: ${error}`); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /16 - Balance Command/src/events/ready/01registerCommands.js: -------------------------------------------------------------------------------- 1 | const { testServer } = require('../../../config.json'); 2 | const areCommandsDifferent = require('../../utils/areCommandsDifferent'); 3 | const getApplicationCommands = require('../../utils/getApplicationCommands'); 4 | const getLocalCommands = require('../../utils/getLocalCommands'); 5 | 6 | module.exports = async (client) => { 7 | try { 8 | const localCommands = getLocalCommands(); 9 | const applicationCommands = await getApplicationCommands( 10 | client, 11 | testServer 12 | ); 13 | 14 | for (const localCommand of localCommands) { 15 | const { name, description, options } = localCommand; 16 | 17 | const existingCommand = await applicationCommands.cache.find( 18 | (cmd) => cmd.name === name 19 | ); 20 | 21 | if (existingCommand) { 22 | if (localCommand.deleted) { 23 | await applicationCommands.delete(existingCommand.id); 24 | console.log(`🗑 Deleted command "${name}".`); 25 | continue; 26 | } 27 | 28 | if (areCommandsDifferent(existingCommand, localCommand)) { 29 | await applicationCommands.edit(existingCommand.id, { 30 | description, 31 | options, 32 | }); 33 | 34 | console.log(`🔁 Edited command "${name}".`); 35 | } 36 | } else { 37 | if (localCommand.deleted) { 38 | console.log( 39 | `⏩ Skipping registering command "${name}" as it's set to delete.` 40 | ); 41 | continue; 42 | } 43 | 44 | await applicationCommands.create({ 45 | name, 46 | description, 47 | options, 48 | }); 49 | 50 | console.log(`👍 Registered command "${name}."`); 51 | } 52 | } 53 | } catch (error) { 54 | console.log(`There was an error: ${error}`); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /16 - Balance Command/src/events/ready/consoleLog.js: -------------------------------------------------------------------------------- 1 | module.exports = (client) => { 2 | console.log(`${client.user.tag} is online.`); 3 | }; 4 | -------------------------------------------------------------------------------- /16 - Balance Command/src/handlers/eventHandler.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('../utils/getAllFiles'); 3 | 4 | module.exports = (client) => { 5 | const eventFolders = getAllFiles(path.join(__dirname, '..', 'events'), true); 6 | 7 | for (const eventFolder of eventFolders) { 8 | let eventFiles = getAllFiles(eventFolder); 9 | eventFiles.sort(); 10 | 11 | const eventName = eventFolder.replace(/\\/g, '/').split('/').pop(); 12 | 13 | client.on(eventName, async (arg) => { 14 | for (const eventFile of eventFiles) { 15 | const eventFunction = require(eventFile); 16 | await eventFunction(client, arg); 17 | } 18 | }); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /16 - Balance Command/src/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { Client, IntentsBitField } = require('discord.js'); 3 | const mongoose = require('mongoose'); 4 | const eventHandler = require('./handlers/eventHandler'); 5 | 6 | const client = new Client({ 7 | intents: [ 8 | IntentsBitField.Flags.Guilds, 9 | IntentsBitField.Flags.GuildMembers, 10 | IntentsBitField.Flags.GuildMessages, 11 | IntentsBitField.Flags.GuildPresences, 12 | IntentsBitField.Flags.MessageContent, 13 | ], 14 | }); 15 | 16 | (async () => { 17 | try { 18 | mongoose.set('strictQuery', false); 19 | await mongoose.connect(process.env.MONGODB_URI); 20 | console.log('Connected to DB.'); 21 | 22 | eventHandler(client); 23 | 24 | client.login(process.env.TOKEN); 25 | } catch (error) { 26 | console.log(`Error: ${error}`); 27 | } 28 | })(); 29 | -------------------------------------------------------------------------------- /16 - Balance Command/src/models/AutoRole.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const autoRoleSchema = new Schema({ 4 | guildId: { 5 | type: String, 6 | required: true, 7 | unique: true, 8 | }, 9 | roleId: { 10 | type: String, 11 | required: true, 12 | }, 13 | }); 14 | 15 | module.exports = model('AutoRole', autoRoleSchema); 16 | -------------------------------------------------------------------------------- /16 - Balance Command/src/models/Level.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const levelSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | xp: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | level: { 17 | type: Number, 18 | default: 0, 19 | }, 20 | }); 21 | 22 | module.exports = model('Level', levelSchema); 23 | -------------------------------------------------------------------------------- /16 - Balance Command/src/models/User.js: -------------------------------------------------------------------------------- 1 | const { Schema, model } = require('mongoose'); 2 | 3 | const userSchema = new Schema({ 4 | userId: { 5 | type: String, 6 | required: true, 7 | }, 8 | guildId: { 9 | type: String, 10 | required: true, 11 | }, 12 | balance: { 13 | type: Number, 14 | default: 0, 15 | }, 16 | lastDaily: { 17 | type: Date, 18 | reqired: true, 19 | }, 20 | }); 21 | 22 | module.exports = model('User', userSchema); 23 | -------------------------------------------------------------------------------- /16 - Balance Command/src/utils/areCommandsDifferent.js: -------------------------------------------------------------------------------- 1 | module.exports = (existingCommand, localCommand) => { 2 | const areChoicesDifferent = (existingChoices, localChoices) => { 3 | for (const localChoice of localChoices) { 4 | const existingChoice = existingChoices?.find( 5 | (choice) => choice.name === localChoice.name 6 | ); 7 | 8 | if (!existingChoice) { 9 | return true; 10 | } 11 | 12 | if (localChoice.value !== existingChoice.value) { 13 | return true; 14 | } 15 | } 16 | return false; 17 | }; 18 | 19 | const areOptionsDifferent = (existingOptions, localOptions) => { 20 | for (const localOption of localOptions) { 21 | const existingOption = existingOptions?.find( 22 | (option) => option.name === localOption.name 23 | ); 24 | 25 | if (!existingOption) { 26 | return true; 27 | } 28 | 29 | if ( 30 | localOption.description !== existingOption.description || 31 | localOption.type !== existingOption.type || 32 | (localOption.required || false) !== existingOption.required || 33 | (localOption.choices?.length || 0) !== 34 | (existingOption.choices?.length || 0) || 35 | areChoicesDifferent( 36 | localOption.choices || [], 37 | existingOption.choices || [] 38 | ) 39 | ) { 40 | return true; 41 | } 42 | } 43 | return false; 44 | }; 45 | 46 | if ( 47 | existingCommand.description !== localCommand.description || 48 | existingCommand.options?.length !== (localCommand.options?.length || 0) || 49 | areOptionsDifferent(existingCommand.options, localCommand.options || []) 50 | ) { 51 | return true; 52 | } 53 | 54 | return false; 55 | }; 56 | -------------------------------------------------------------------------------- /16 - Balance Command/src/utils/calculateLevelXp.js: -------------------------------------------------------------------------------- 1 | module.exports = (level) => 100 * level || 1; 2 | -------------------------------------------------------------------------------- /16 - Balance Command/src/utils/getAllFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (directory, foldersOnly = false) => { 5 | let fileNames = []; 6 | 7 | const files = fs.readdirSync(directory, { withFileTypes: true }); 8 | 9 | for (const file of files) { 10 | const filePath = path.join(directory, file.name); 11 | 12 | if (foldersOnly) { 13 | if (file.isDirectory()) { 14 | fileNames.push(filePath); 15 | } 16 | } else { 17 | if (file.isFile()) { 18 | fileNames.push(filePath); 19 | } 20 | } 21 | } 22 | 23 | return fileNames; 24 | }; 25 | -------------------------------------------------------------------------------- /16 - Balance Command/src/utils/getApplicationCommands.js: -------------------------------------------------------------------------------- 1 | module.exports = async (client, guildId) => { 2 | let applicationCommands; 3 | 4 | if (guildId) { 5 | const guild = await client.guilds.fetch(guildId); 6 | applicationCommands = guild.commands; 7 | } else { 8 | applicationCommands = await client.application.commands; 9 | } 10 | 11 | await applicationCommands.fetch(); 12 | return applicationCommands; 13 | }; 14 | -------------------------------------------------------------------------------- /16 - Balance Command/src/utils/getLocalCommands.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getAllFiles = require('./getAllFiles'); 3 | 4 | module.exports = (exceptions = []) => { 5 | let localCommands = []; 6 | 7 | const commandCategories = getAllFiles( 8 | path.join(__dirname, '..', 'commands'), 9 | true 10 | ); 11 | 12 | for (const commandCategory of commandCategories) { 13 | const commandFiles = getAllFiles(commandCategory); 14 | 15 | for (const commandFile of commandFiles) { 16 | const commandObject = require(commandFile); 17 | 18 | if (exceptions.includes(commandObject.name)) { 19 | continue; 20 | } 21 | 22 | localCommands.push(commandObject); 23 | } 24 | } 25 | 26 | return localCommands; 27 | }; 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Avraj Sahota 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Discord.js V14 Series](https://djs14.underctrl.io/) 2 | --------------------------------------------------------------------------------