diff --git a/.gitignore b/.gitignore index 40b878d..cf7c602 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +config.json \ No newline at end of file diff --git a/README.md b/README.md index 3a37214..16f1fdf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # Minzbot - General Purpose Bot -Successor of the Discord.py implementation \ No newline at end of file +Successor of the Discord.py implementation +https://discord.com/api/oauth2/authorize?client_id=816228963285860373&permissions=58273042529745&scope=bot \ No newline at end of file diff --git a/commands/utility/ping.js b/commands/utility/ping.js new file mode 100644 index 0000000..50f85b5 --- /dev/null +++ b/commands/utility/ping.js @@ -0,0 +1,11 @@ +const { SlashCommandBuilder } = require('discord.js'); + +module.exports = { + category: 'utility', + data: new SlashCommandBuilder() + .setName('ping') + .setDescription('Replies with Pong!'), + async execute(interaction) { + await interaction.reply('Pong!'); + }, +}; \ No newline at end of file diff --git a/commands/utility/reload.js b/commands/utility/reload.js new file mode 100644 index 0000000..2116fe4 --- /dev/null +++ b/commands/utility/reload.js @@ -0,0 +1,31 @@ +const { SlashCommandBuilder } = require('discord.js'); + +module.exports = { + data: new SlashCommandBuilder() + .setName('reload') + .setDescription('Reloads a command.') + .addStringOption(option => + option.setName('command') + .setDescription('The command to reload.') + .setRequired(true)), + async execute(interaction) { + const commandName = interaction.options.getString('command', true).toLowerCase(); + const command = interaction.client.commands.get(commandName); + + if (!command) { + return interaction.reply(`There is no command with name \`${commandName}\`!`); + } + + delete require.cache[require.resolve(`../${command.category}/${command.data.name}.js`)]; + + try { + interaction.client.commands.delete(command.data.name); + const newCommand = require(`../${command.category}/${command.data.name}.js`); + interaction.client.commands.set(newCommand.data.name, newCommand); + await interaction.reply(`Command \`${newCommand.data.name}\` was reloaded!`); + } catch (error) { + console.error(error); + await interaction.reply(`There was an error while reloading a command \`${command.data.name}\`:\n\`${error.message}\``); + } + }, +}; \ No newline at end of file diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..5ccf867 --- /dev/null +++ b/config.json.example @@ -0,0 +1,5 @@ +{ + "token": "", + "clientId": "", + "guildId": "" +} \ No newline at end of file diff --git a/deploy-commands.js b/deploy-commands.js new file mode 100644 index 0000000..bb331f7 --- /dev/null +++ b/deploy-commands.js @@ -0,0 +1,46 @@ +const { REST, Routes } = require('discord.js'); +const { clientId, guildId, token } = require('./config.json'); +const fs = require('node:fs'); +const path = require('node:path'); + +const commands = []; +// Grab all the command files from the commands directory you created earlier +const foldersPath = path.join(__dirname, 'commands'); +const commandFolders = fs.readdirSync(foldersPath); + +for (const folder of commandFolders) { + // Grab all the command files from the commands directory you created earlier + const commandsPath = path.join(foldersPath, folder); + const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); + // Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment + for (const file of commandFiles) { + const filePath = path.join(commandsPath, file); + const command = require(filePath); + if ('data' in command && 'execute' in command) { + commands.push(command.data.toJSON()); + } else { + console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`); + } + } +} + +// Construct and prepare an instance of the REST module +const rest = new REST().setToken(token); + +// and deploy your commands! +(async () => { + try { + console.log(`Started refreshing ${commands.length} application (/) commands.`); + + // The put method is used to fully refresh all commands in the guild with the current set + const data = await rest.put( + Routes.applicationGuildCommands(clientId, guildId), + { body: commands }, + ); + + console.log(`Successfully reloaded ${data.length} application (/) commands.`); + } catch (error) { + // And of course, make sure you catch and log any errors! + console.error(error); + } +})(); \ No newline at end of file diff --git a/events/interactionCreate.js b/events/interactionCreate.js new file mode 100644 index 0000000..ce0adc6 --- /dev/null +++ b/events/interactionCreate.js @@ -0,0 +1,22 @@ +const { Events } = require('discord.js'); + +module.exports = { + name: Events.InteractionCreate, + async execute(interaction) { + if (!interaction.isChatInputCommand()) return; + + const command = interaction.client.commands.get(interaction.commandName); + + if (!command) { + console.error(`No command matching ${interaction.commandName} was found.`); + return; + } + + try { + await command.execute(interaction); + } catch (error) { + console.error(`Error executing ${interaction.commandName}`); + console.error(error); + } + }, +}; \ No newline at end of file diff --git a/events/messageCreate.js b/events/messageCreate.js new file mode 100644 index 0000000..9b4bfd0 --- /dev/null +++ b/events/messageCreate.js @@ -0,0 +1,8 @@ +const { Events } = require('discord.js'); + +module.exports = { + name: Events.MessageCreate, + async execute(message) { + console.log(`${message.author.globalName}: ${message.content}`); + }, +}; \ No newline at end of file diff --git a/events/ready.js b/events/ready.js new file mode 100644 index 0000000..2d254c5 --- /dev/null +++ b/events/ready.js @@ -0,0 +1,9 @@ +const { Events } = require('discord.js'); + +module.exports = { + name: Events.ClientReady, + once: true, + execute(client) { + console.log(`Ready! Logged in as ${client.user.tag}`); + }, +}; \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..8354477 --- /dev/null +++ b/main.js @@ -0,0 +1,39 @@ +const fs = require('node:fs'); +const path = require('node:path'); +const { Client, Collection, Events, GatewayIntentBits } = require('discord.js'); +const { token } = require('./config.json'); + +const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages] }); + +client.commands = new Collection(); +const foldersPath = path.join(__dirname, 'commands'); +const commandFolders = fs.readdirSync(foldersPath); + +for (const folder of commandFolders) { + const commandsPath = path.join(foldersPath, folder); + const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); + for (const file of commandFiles) { + const filePath = path.join(commandsPath, file); + const command = require(filePath); + if ('data' in command && 'execute' in command) { + client.commands.set(command.data.name, command); + } else { + console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`); + } + } +} + +const eventsPath = path.join(__dirname, 'events'); +const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js')); + +for (const file of eventFiles) { + const filePath = path.join(eventsPath, file); + const event = require(filePath); + if (event.once) { + client.once(event.name, (...args) => event.execute(...args)); + } else { + client.on(event.name, (...args) => event.execute(...args)); + } +} + +client.login(token); \ No newline at end of file diff --git a/package.json b/package.json index efb5b2c..1b9c6ad 100644 --- a/package.json +++ b/package.json @@ -2,5 +2,15 @@ "dependencies": { "discord.js": "^14.13.0", "dotenv": "^16.3.1" - } + }, + "name": "minzbot", + "description": "Successor of the Discord.py implementation", + "version": "1.0.0", + "main": "main.js", + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Minz💕", + "license": "ISC" }