diff --git a/eslint.config.mjs b/eslint.config.mjs index 7a0bbf6..bd929be 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,6 +1,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import js from "@eslint/js"; +import globals from "globals"; import { FlatCompat } from "@eslint/eslintrc"; import tsParser from "@typescript-eslint/parser"; import typescriptEslint from "@typescript-eslint/eslint-plugin"; @@ -30,6 +31,9 @@ export default [ parserOptions: { project: "./tsconfig.json", }, + globals: { + ...globals.node, + }, }, rules: { @@ -117,6 +121,9 @@ export default [ "space-unary-ops": "error", "spaced-comment": "error", yoda: "error", + + "no-redeclare": "off", + "no-unused-vars": "off", // This is causing issues }, }, ]; diff --git a/package.json b/package.json index 8d58c39..076c9bd 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@typescript-eslint/parser": "^8.18.1", "drizzle-kit": "^0.30.1", "eslint": "^9.17.0", + "globals": "^15.14.0", "ts-node": "^10.9.2", "tsx": "^4.19.2", "typescript": "^5.7.2" diff --git a/src/commands/members.ts b/src/commands/util/members.ts similarity index 94% rename from src/commands/members.ts rename to src/commands/util/members.ts index ff27647..dde0298 100644 --- a/src/commands/members.ts +++ b/src/commands/util/members.ts @@ -3,7 +3,7 @@ import { CommandInteraction, EmbedBuilder, } from 'discord.js'; -import { getAllMembers } from '../util/db.js'; +import { getAllMembers } from '../../util/db.js'; interface Command { data: Omit; diff --git a/src/commands/ping.ts b/src/commands/util/ping.ts similarity index 100% rename from src/commands/ping.ts rename to src/commands/util/ping.ts diff --git a/src/commands/server.ts b/src/commands/util/server.ts similarity index 100% rename from src/commands/server.ts rename to src/commands/util/server.ts diff --git a/src/commands/user-info.ts b/src/commands/util/user-info.ts similarity index 97% rename from src/commands/user-info.ts rename to src/commands/util/user-info.ts index 9a8c160..8bb2f57 100644 --- a/src/commands/user-info.ts +++ b/src/commands/util/user-info.ts @@ -4,7 +4,7 @@ import { EmbedBuilder, SlashCommandOptionsOnlyBuilder, } from 'discord.js'; -import { getMember } from '../util/db.js'; +import { getMember } from '../../util/db.js'; interface Command { data: SlashCommandOptionsOnlyBuilder; diff --git a/src/discord-bot.ts b/src/discord-bot.ts index 076a437..b34e753 100644 --- a/src/discord-bot.ts +++ b/src/discord-bot.ts @@ -1,9 +1,8 @@ import fs from 'node:fs'; -import path from 'node:path'; -import { Client, Collection, Events, GatewayIntentBits } from 'discord.js'; +import { Client, Collection, Events, GatewayIntentBits, GuildMember } from 'discord.js'; import { deployCommands } from './util/deployCommand.js'; -import { getMember, removeMember, setMembers } from './util/db.js'; +import { removeMember, setMembers } from './util/db.js'; const config = JSON.parse(fs.readFileSync('./config.json', 'utf8')); const { token, guildId } = config; @@ -14,51 +13,32 @@ const client: any = new Client({ client.commands = new Collection(); try { - const __dirname = path.resolve(); - - const commandsPath = path.join(__dirname, '/target/commands/'); - const commandFiles = fs - .readdirSync(commandsPath) - .filter((file) => file.endsWith('.js')); - - for (const file of commandFiles) { - const filePath = path.join('file://', commandsPath, file); - const commandModule = await import(filePath); - const command = commandModule.default; - - if ( - command instanceof Object && - 'data' in command && - 'execute' in command - ) { + const commands = await deployCommands(); + if (!commands) { + throw new Error('No commands found.'); + } + commands.forEach(async (command) => { + try { client.commands.set(command.data.name, command); } - else { - console.log( - `[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.` - ); + catch (error: any) { + console.error(`Error while creating command: ${error}`); } - } + }); + console.log('Commands registered successfully.'); } catch (error: any) { - console.log(`Error while getting commands up: ${error}`); + console.error(`Error while registering commands: ${error}`); } -try { - await deployCommands(); -} -catch (error: any) { - console.log(`Error while registering commands: ${error}`); -} - -client.once(Events.ClientReady, async (c: any) => { +client.once(Events.ClientReady, async (c: Client) => { const guild = await client.guilds.fetch(guildId); const members = await guild.members.fetch(); const nonBotMembers = members.filter((member: any) => !member.user.bot); await setMembers(nonBotMembers); - console.log(`Ready! Logged in as ${c.user.tag}`); + console.log(`Ready! Logged in as ${c!.user!.tag}`); }); client.on(Events.InteractionCreate, async (interaction: any) => { @@ -91,15 +71,26 @@ client.on(Events.InteractionCreate, async (interaction: any) => { } }); -client.on(Events.GuildMemberAdd, async () => { +client.on(Events.GuildMemberAdd, async (member: GuildMember) => { const guild = await client.guilds.fetch(guildId); const members = await guild.members.fetch(); - const nonBotMembers = members.filter((member: any) => !member.user.bot); + const nonBotMembers = members.filter((dbMember: any) => !dbMember.user.bot); - await setMembers(nonBotMembers); + // TODO: Move this to the config file + const welcomeChannel = guild.channels.cache.get('1007949346031026186'); + + try { + await setMembers(nonBotMembers); + // TODO: Move this to config file + await welcomeChannel.send(`Welcome to the server, ${member.user.username}!`); + await member.user.send('Welcome to the Poixpixel Discord server!'); + } + catch (error: any) { + console.error(`Error while adding member: ${error}`); + } }); -client.on(Events.GuildMemberRemove, async (member: any) => { +client.on(Events.GuildMemberRemove, async (member: GuildMember) => { await removeMember(member.user.id); }); diff --git a/src/util/deployCommand.ts b/src/util/deployCommand.ts index 4ed55ed..5f5506d 100644 --- a/src/util/deployCommand.ts +++ b/src/util/deployCommand.ts @@ -1,17 +1,28 @@ -import { REST, Routes } from 'discord.js'; import fs from 'fs'; import path from 'path'; -const config = JSON.parse(fs.readFileSync('./config.json', 'utf8')); -const { token, clientId, guildId } = config; - const __dirname = path.resolve(); const commandsPath = path.join(__dirname, 'target', 'commands'); -const commandFiles = fs - .readdirSync(commandsPath) - .filter((file) => file.endsWith('.js')); -const rest = new REST({ version: '9' }).setToken(token); +const getFilesRecursively = (directory: string): string[] => { + const files: string[] = []; + const filesInDirectory = fs.readdirSync(directory); + + for (const file of filesInDirectory) { + const filePath = path.join(directory, file); + + if (fs.statSync(filePath).isDirectory()) { + files.push(...getFilesRecursively(filePath)); + } + else if (file.endsWith('.js')) { + files.push(filePath); + } + } + + return files; +}; + +const commandFiles = getFilesRecursively(commandsPath); export const deployCommands = async () => { try { @@ -20,16 +31,19 @@ export const deployCommands = async () => { ); const commands = commandFiles.map(async (file) => { - const filePath = path.join('file://', commandsPath, file); - const commandModule = await import(filePath); + const commandModule = await import(`file://${file}`); const command = commandModule.default; - if (command instanceof Object && 'data' in command) { - return command.data.toJSON(); + if ( + command instanceof Object && + 'data' in command && + 'execute' in command + ) { + return command; } else { - console.log( - `[WARNING] The command at ${filePath} is missing a required "data" property.` + console.warn( + `[WARNING] The command at ${file} is missing a required "data" or "execute" property.` ); return null; } @@ -39,14 +53,7 @@ export const deployCommands = async () => { commands.filter((command) => command !== null) ); - const data: any = await rest.put( - Routes.applicationGuildCommands(clientId, guildId), - { body: validCommands } - ); - - console.log( - `Successfully reloaded ${data.length} application (/) commands.` - ); + return validCommands; } catch (error) { console.error(error); diff --git a/yarn.lock b/yarn.lock index 9b2e44f..4e3c941 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2113,6 +2113,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:^15.14.0": + version: 15.14.0 + resolution: "globals@npm:15.14.0" + checksum: 10c0/039deb8648bd373b7940c15df9f96ab7508fe92b31bbd39cbd1c1a740bd26db12457aa3e5d211553b234f30e9b1db2fee3683012f543a01a6942c9062857facb + languageName: node + linkType: hard + "graceful-fs@npm:^4.2.6": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" @@ -2803,6 +2810,7 @@ __metadata: drizzle-kit: "npm:^0.30.1" drizzle-orm: "npm:^0.38.2" eslint: "npm:^9.17.0" + globals: "npm:^15.14.0" pg: "npm:^8.13.1" ts-node: "npm:^10.9.2" tsx: "npm:^4.19.2"