diff --git a/.eslintrc.json b/.eslintrc.json index a5224f2..91ec9e3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,47 +1,47 @@ -{ - "extends": "eslint:recommended", - "env": { - "node": true, - "es6": true - }, - "parserOptions": { - "ecmaVersion": 2021 - }, - "rules": { - "arrow-spacing": ["warn", { "before": true, "after": true }], - "comma-dangle": ["error", "always-multiline"], - "comma-spacing": "error", - "comma-style": "error", - "curly": ["error", "multi-line", "consistent"], - "dot-location": ["error", "property"], - "handle-callback-err": "off", - "keyword-spacing": "error", - "max-nested-callbacks": ["error", { "max": 4 }], - "max-statements-per-line": ["error", { "max": 2 }], - "no-console": "off", - "no-empty-function": "error", - "no-floating-decimal": "error", - "no-inline-comments": "error", - "no-lonely-if": "error", - "no-multi-spaces": "error", - "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], - "no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], - "no-trailing-spaces": ["error"], - "no-var": "error", - "object-curly-spacing": ["error", "always"], - "prefer-const": "error", - "quotes": ["error", "single"], - "semi": ["error", "always"], - "space-before-blocks": "error", - "space-before-function-paren": ["error", { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - }], - "space-in-parens": "error", - "space-infix-ops": "error", - "space-unary-ops": "error", - "spaced-comment": "error", - "yoda": "error" - } -} \ No newline at end of file +// { +// "extends": "eslint:recommended", +// "env": { +// "node": true, +// "es6": true +// }, +// "parserOptions": { +// "ecmaVersion": 2021 +// }, +// "rules": { +// // "arrow-spacing": ["warn", { "before": true, "after": true }], +// // "comma-dangle": ["error", "always-multiline"], +// // "comma-spacing": "error", +// // "comma-style": "error", +// // "curly": ["error", "multi-line", "consistent"], +// // "dot-location": ["error", "property"], +// // "handle-callback-err": "off", +// // "keyword-spacing": "error", +// // "max-nested-callbacks": ["error", { "max": 4 }], +// // "max-statements-per-line": ["error", { "max": 2 }], +// // "no-console": "off", +// // "no-empty-function": "error", +// // "no-floating-decimal": "error", +// // "no-inline-comments": "error", +// // "no-lonely-if": "error", +// // "no-multi-spaces": "error", +// // "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], +// // "no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], +// // "no-trailing-spaces": ["error"], +// // "no-var": "error", +// // "object-curly-spacing": ["error", "always"], +// // "prefer-const": "error", +// // "quotes": ["error", "single"], +// // "semi": ["error", "always"], +// // "space-before-blocks": "error", +// // "space-before-function-paren": ["error", { +// // "anonymous": "never", +// // "named": "never", +// // "asyncArrow": "always" +// // }], +// // "space-in-parens": "error", +// // "space-infix-ops": "error", +// // "space-unary-ops": "error", +// // "spaced-comment": "error", +// // "yoda": "error" +// } +// } \ No newline at end of file diff --git a/commands/100-games/beatGame.js b/commands/100-games/beatGame.js index fdcfcd1..4401e67 100644 --- a/commands/100-games/beatGame.js +++ b/commands/100-games/beatGame.js @@ -1,19 +1,27 @@ -const { SlashCommandBuilder } = require('discord.js'); +import { SlashCommandBuilder, EmbedBuilder } from 'discord.js'; -module.exports = { - data: new SlashCommandBuilder() - .setName('beatgame') - .setDescription('Log a game that you have beat towards the 100 game challenge!') - .addStringOption(option => option.setName('gamename').setDescription('The name of the game.')) - .addNumberOption(option => option.setName('gameid').setDescription('The IGDB game id.')) - .addStringOption(option => option.setName('datestarted').setDescription('The date you started playing the game (today if empty).')) - .addStringOption(option => option.setName('datebeaten').setDescription('The date you beat the game (today if empty).')), - async execute(interaction) { - const gamename = interaction.options.getString('gamename'); - const gameid = interaction.options.getNumber('gameid'); +export const data = new SlashCommandBuilder() + .setName('beatgame') + .setDescription('Log a game that you have beat towards the 100 game challenge!') + .addStringOption(option => option.setName('gamename').setDescription('The name of the game.')) + .addNumberOption(option => option.setName('gameid').setDescription('The IGDB game id.').setMinValue(0)) + .addStringOption(option => option.setName('datestarted').setDescription('The date you started playing the game (today if empty).')) + .addStringOption(option => option.setName('datebeaten').setDescription('The date you beat the game (today if empty).')); - if (!gamename && !gameid) return interaction.reply('No gamename or gameid supplied, please supply an option to register a game!'); - if (gameid) return interaction.reply(`Game ID "${gameid}" has been beaten, Game logged!`); - return interaction.reply(`Game "${gamename}" has been beaten, Game logged!`); - }, -}; \ No newline at end of file +export async function execute(interaction) { + const gamename = interaction.options.getString('gamename'); + const gameid = interaction.options.getNumber('gameid'); + + if (!gamename && !gameid) return interaction.reply('No gamename or gameid supplied, please supply an option to register a game!'); + + const embed = new EmbedBuilder() + .setColor(0xFFD700) + .setAuthor({ name: `${interaction.user.displayName} beat a new game!`, iconURL: interaction.user.avatarURL() }) + .setTitle('osu! beaten') + .setThumbnail('https://upload.wikimedia.org/wikipedia/en/8/8d/Dark_Souls_Cover_Art.jpg') + .setDescription(`${interaction.user.displayName} has beaten 69 games, they have 31 games remaining.`) + .setFooter({ text: 'The Ochulus • 100 Games Challenge', iconURL: interaction.client.user.avatarURL() }) + .setTimestamp(); + + return interaction.reply({ embeds: [embed] }); +} \ No newline at end of file diff --git a/commands/testing/avatar.js b/commands/testing/avatar.js index f6384b4..e787ab4 100644 --- a/commands/testing/avatar.js +++ b/commands/testing/avatar.js @@ -1,13 +1,12 @@ -const { SlashCommandBuilder } = require('discord.js'); +import { SlashCommandBuilder } from 'discord.js'; -module.exports = { - data: new SlashCommandBuilder() - .setName('avatar') - .setDescription('Get the avatar URL of the selected user, or your own avatar.') - .addUserOption(option => option.setName('user').setDescription('The user\'s avatar to show')), - async execute(interaction) { - const user = interaction.options.getUser('user'); - if (user) return interaction.reply(`${user.username}'s avatar: ${user.displayAvatarURL()}`); - return interaction.reply(`Your avatar: ${interaction.user.displayAvatarURL()}`); - }, -}; \ No newline at end of file +export const data = new SlashCommandBuilder() + .setName('avatar') + .setDescription('Get the avatar URL of the selected user, or your own avatar.') + .addUserOption(option => option.setName('user').setDescription('The user\'s avatar to show')); + +export async function execute(interaction) { + const user = interaction.options.getUser('user'); + if (user) return interaction.reply(`${user.username}'s avatar: ${user.displayAvatarURL()}`); + return interaction.reply(`Your avatar: ${interaction.user.displayAvatarURL()}`); +} \ No newline at end of file diff --git a/commands/testing/ping.js b/commands/testing/ping.js index a27265b..6a6e003 100644 --- a/commands/testing/ping.js +++ b/commands/testing/ping.js @@ -1,10 +1,9 @@ -const { SlashCommandBuilder } = require('discord.js'); +import { SlashCommandBuilder } from 'discord.js'; -module.exports = { - data: new SlashCommandBuilder() - .setName('ping') - .setDescription('Replies with pong!'), - async execute(interaction) { - await interaction.reply('Pong!'); - }, -}; \ No newline at end of file +export const data = new SlashCommandBuilder() + .setName('ping') + .setDescription('Replies with pong!'); + +export async function execute(interaction) { + await interaction.reply('Pong!'); +} \ No newline at end of file diff --git a/commands/testing/reload.js b/commands/testing/reload.js index bf4836a..72ce3d9 100644 --- a/commands/testing/reload.js +++ b/commands/testing/reload.js @@ -1,31 +1,30 @@ -const { SlashCommandBuilder } = require('discord.js'); +import { SlashCommandBuilder } from 'discord.js'; -module.exports = { - data: new SlashCommandBuilder() +export const data = new SlashCommandBuilder() .setName('reload') .setDescription('Reload 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); + .setRequired(true)); - if (!command) { - return interaction.reply(`There is no command with name \`${commandName}\`!`); - } +export async function execute(interaction) { + const commandName = interaction.options.getString('command', true).toLowerCase(); + const command = interaction.client.commands.get(commandName); - delete require.cache[require.resolve(`./${command.data.name}.js`)]; + if (!command) { + return interaction.reply(`There is no command with name \`${commandName}\`!`); + } - try { - interaction.client.commands.delete(command.data.name); - const newCommand = require(`./${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 + delete require.cache[require.resolve(`./${command.data.name}.js`)]; + + try { + interaction.client.commands.delete(command.data.name); + const newCommand = require(`./${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/commands/testing/server.js b/commands/testing/server.js index 2ee386a..089b03c 100644 --- a/commands/testing/server.js +++ b/commands/testing/server.js @@ -1,10 +1,9 @@ -const { SlashCommandBuilder } = require('discord.js'); +import { SlashCommandBuilder } from 'discord.js'; -module.exports = { - data: new SlashCommandBuilder() - .setName('server') - .setDescription('Provides information about the server.'), - async execute(interaction) { - await interaction.reply(`This server is ${interaction.guild.name} and has ${interaction.guild.memberCount} members.`); - }, -}; \ No newline at end of file +export const data = new SlashCommandBuilder() +.setName('server') +.setDescription('Provides information about the server.'); + +export async function execute(interaction) { + await interaction.reply(`This server is ${interaction.guild.name} and has ${interaction.guild.memberCount} members.`); +} \ No newline at end of file diff --git a/commands/testing/user.js b/commands/testing/user.js index 1f309b6..e9fb466 100644 --- a/commands/testing/user.js +++ b/commands/testing/user.js @@ -1,12 +1,12 @@ -const { SlashCommandBuilder } = require('discord.js'); +import { SlashCommandBuilder } from 'discord.js'; -module.exports = { - data: new SlashCommandBuilder() - .setName('user') - .setDescription('Provides information about the user.'), - async execute(interaction) { +export const data = new SlashCommandBuilder() + .setName('user') + .setDescription('Provides information about the user.'); + +export async function execute(interaction) { // interaction.user is the object representing the user who ran the command // interaction.member is the GuildMember object, which represents the user in the specific guild await interaction.reply(`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`); - }, -}; \ No newline at end of file + +} \ No newline at end of file diff --git a/deploy-commands.js b/deploy-commands.js index bb331f7..e0a109b 100644 --- a/deploy-commands.js +++ b/deploy-commands.js @@ -1,10 +1,17 @@ -const { REST, Routes } = require('discord.js'); -const { clientId, guildId, token } = require('./config.json'); -const fs = require('node:fs'); -const path = require('node:path'); +import { REST, Routes } from 'discord.js'; +//import { clientId, guildId, token } from './config.json'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'url'; +import { pathToFileURL } from 'node:url'; + +import { config } from 'dotenv'; +config(); const commands = []; // Grab all the command files from the commands directory you created earlier +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const foldersPath = path.join(__dirname, 'commands'); const commandFolders = fs.readdirSync(foldersPath); @@ -15,7 +22,7 @@ for (const folder of commandFolders) { // 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); + const command = await import(pathToFileURL(filePath)); if ('data' in command && 'execute' in command) { commands.push(command.data.toJSON()); } else { @@ -25,7 +32,7 @@ for (const folder of commandFolders) { } // Construct and prepare an instance of the REST module -const rest = new REST().setToken(token); +const rest = new REST().setToken(process.env.token); // and deploy your commands! (async () => { @@ -34,7 +41,7 @@ const rest = new REST().setToken(token); // 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), + Routes.applicationGuildCommands(process.env.clientId, process.env.guildId), { body: commands }, ); diff --git a/events/interactionCreate.js b/events/interactionCreate.js index 4859005..84fb60b 100644 --- a/events/interactionCreate.js +++ b/events/interactionCreate.js @@ -1,51 +1,57 @@ -const { Events, Collection } = require('discord.js'); +import { Events, Collection } from 'discord.js'; -module.exports = { - name: Events.InteractionCreate, - async execute(interaction) { - if (!interaction.isChatInputCommand()) return; +export const name = Events.InteractionCreate; - console.log(interaction); +export async function execute(interaction) { + if (!interaction.isChatInputCommand()) return; - const command = interaction.client.commands.get(interaction.commandName); + console.log(interaction); - if (!command) { - console.error(`No command matching ${interaction.commandName} was found.`); - return; + const command = interaction.client.commands.get(interaction.commandName); + + if (!command) { + console.error(`No command matching ${interaction.commandName} was found.`); + return; + } + + const { cooldowns } = interaction.client; + + if (!cooldowns.has(command.data.name)) { + cooldowns.set(command.data.name, new Collection()); + } + + const now = Date.now(); + const timestamps = cooldowns.get(command.data.name); + const defaultCooldownDuration = 3; + const cooldownAmount = (command.cooldown ?? defaultCooldownDuration) * 1000; + + if (timestamps.has(interaction.user.id)) { + const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount; + + if (now < expirationTime) { + const expiredTimestamp = Math.round(expirationTime / 1000); + return interaction.reply({ content: `Please wait, you are on a cooldown for \`${command.data.name}\`. You can use it again .`, ephemeral: true }); } + } - const { cooldowns } = interaction.client; + timestamps.set(interaction.user.id, now); + setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); - if (!cooldowns.has(command.data.name)) { - cooldowns.set(command.data.name, new Collection()); + try { + await command.execute(interaction); + } catch (error) { + console.error(error); + if (interaction.replied || interaction.deferred) { + await interaction.followUp({ content: 'There was an error while executing the command!', ephemeral: true }); + } else { + await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); } + } +} - const now = Date.now(); - const timestamps = cooldowns.get(command.data.name); - const defaultCooldownDuration = 3; - const cooldownAmount = (command.cooldown ?? defaultCooldownDuration) * 1000; - - if (timestamps.has(interaction.user.id)) { - const expirationTime = timestamps.get(interaction.user.id) + cooldownAmount; - - if (now < expirationTime) { - const expiredTimestamp = Math.round(expirationTime / 1000); - return interaction.reply({ content: `Please wait, you are on a cooldown for \`${command.data.name}\`. You can use it again .`, ephemeral: true }); - } - } - - timestamps.set(interaction.user.id, now); - setTimeout(() => timestamps.delete(interaction.user.id), cooldownAmount); - - try { - await command.execute(interaction); - } catch (error) { - console.error(error); - if (interaction.replied || interaction.deferred) { - await interaction.followUp({ content: 'There was an error while executing the command!', ephemeral: true }); - } else { - await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); - } - } - }, -}; \ No newline at end of file +// module.exports = { +// name: Events.InteractionCreate, +// async execute(interaction) { + +// }, +// }; \ No newline at end of file diff --git a/events/ready.js b/events/ready.js index 0f49430..5d5eeb8 100644 --- a/events/ready.js +++ b/events/ready.js @@ -1,9 +1,9 @@ -const { Events } = require('discord.js'); +import { Events } from 'discord.js'; -module.exports = { - name: Events.ClientReady, - once: true, - execute(client) { +export const name = Events.ClientReady; + +export const once = true; + +export async function execute(client) { console.log(`Ready! Logged in as ${client.user.tag}`); - }, -}; \ No newline at end of file +} \ No newline at end of file diff --git a/index.js b/index.js index 556e2ca..baf8a1c 100644 --- a/index.js +++ b/index.js @@ -1,8 +1,12 @@ // Require the necessary discord.js classes -const fs = require('node:fs'); -const path = require('node:path'); -const { Client, Collection, GatewayIntentBits } = require('discord.js'); -const { token } = require('./config.json'); +import fs from 'node:fs'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { fileURLToPath } from 'url'; +import { Client, Collection, GatewayIntentBits } from 'discord.js'; + +import { config } from 'dotenv'; +config(); // Create a new client instance const client = new Client({ intents: [GatewayIntentBits.Guilds] }); @@ -10,6 +14,8 @@ const client = new Client({ intents: [GatewayIntentBits.Guilds] }); client.commands = new Collection(); client.cooldowns = new Collection(); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const foldersPath = path.join(__dirname, 'commands'); const commandFolders = fs.readdirSync(foldersPath); @@ -19,7 +25,7 @@ for (const folder of commandFolders) { for (const file of commandFiles) { const filePath = path.join(commandsPath, file); - const command = require(filePath); + const command = await import(pathToFileURL(filePath)); // Set a new item in the collection with the key as the command name and the value as the exported module if ('data' in command && 'execute' in command) { @@ -35,7 +41,7 @@ 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); + const event = await import(pathToFileURL(filePath)); if (event.once) { client.once(event.name, (...args) => event.execute(...args)); } else { @@ -43,5 +49,23 @@ for (const file of eventFiles) { } } -// Log in to Discord with your client's token -client.login(token); \ No newline at end of file +fetch( + `https://id.twitch.tv/oauth2/token?client_id=${process.env.igdbClientId}&client_secret=${process.env.igdbClientSecret}&grant_type=client_credentials`, + { + method: 'POST', + }, +) +.then(r => r.json().then(data => ({ status: r.status, headers: r.headers, body: data }))) +.then(resp => { + if (resp.status != 200) { + console.log('Failed with ', resp.status, resp.body); + return; + } + console.log(resp.body.access_token); +}) +.catch(err => { + console.error(err); +}) +.finally(); + +client.login(process.env.token); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 80b2aec..48654bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "discord.js": "^14.14.1" + "discord.js": "^14.14.1", + "dotenv": "^16.3.1" }, "devDependencies": { "eslint": "^8.53.0" @@ -526,6 +527,17 @@ "node": ">=6.0.0" } }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", diff --git a/package.json b/package.json index ad6dd30..e41e5d9 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,12 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, + "type": "module", "author": "", "license": "ISC", "dependencies": { - "discord.js": "^14.14.1" + "discord.js": "^14.14.1", + "dotenv": "^16.3.1" }, "devDependencies": { "eslint": "^8.53.0"