diff --git a/Procfile b/Procfile
index 8753747..973094b 100644
--- a/Procfile
+++ b/Procfile
@@ -1 +1 @@
-worker: node bot.js
\ No newline at end of file
+worker: node index.js
\ No newline at end of file
diff --git a/README.md b/README.md
index 05979be..8375619 100644
--- a/README.md
+++ b/README.md
@@ -69,5 +69,5 @@ node bot.js
### Usage
-- **!verify your_email@example.com**: Sends a verification code to the provided email.
-- **!code your_code**: Validates the provided verification code.
+- **/verify your_email@example.com**: Sends a verification code to the provided email.
+- **/code your_code**: Validates the provided verification code.
diff --git a/bot.js b/bot.js
deleted file mode 100644
index 8150c79..0000000
--- a/bot.js
+++ /dev/null
@@ -1,206 +0,0 @@
-require("dotenv").config();
-const { Client, GatewayIntentBits } = require("discord.js");
-const nodemailer = require("nodemailer");
-const mongoose = require("mongoose");
-const VerificationCode = require("./models/VerificationCode");
-
-const client = new Client({
- intents: [
- GatewayIntentBits.Guilds,
- GatewayIntentBits.GuildMessages,
- GatewayIntentBits.MessageContent,
- ],
-});
-
-const VERIFIED_ROLE_NAME = process.env.VERIFIED_ROLE_NAME; // Role name to assign after verification
-const EMAIL_DOMAINS = process.env.EMAIL_DOMAINS.split(","); // Domains to verify against, converted to an array
-const GUILD_ID = process.env.GUILD_ID; // Guild ID to restrict the bot
-const VERIFICATION_CHANNEL_NAME = process.env.VERIFICATION_CHANNEL_NAME; // Channel name to restrict the bot
-
-// Connect to MongoDB
-mongoose
- .connect(process.env.MONGODB_URI)
- .then(() => console.log("Connected to MongoDB"))
- .catch((err) => console.error("Failed to connect to MongoDB", err));
-
-// Setup Nodemailer
-const transporter = nodemailer.createTransport({
- service: "Gmail",
- auth: {
- user: process.env.EMAIL_USER,
- pass: process.env.EMAIL_PASS,
- },
-});
-
-client.once("ready", () => {
- console.log(`Logged in as ${client.user.tag}`);
-});
-
-client.on("messageCreate", async (message) => {
- try {
- // Ensure bot only operates in the designated channel
- const verificationChannel = message.guild.channels.cache.find(
- (channel) => channel.name === VERIFICATION_CHANNEL_NAME
- );
-
- if (!message.guild || !verificationChannel) {
- console.error("Guild or verification channel not found.");
- return;
- }
-
- if (message.author.bot || message.channel.id !== verificationChannel.id)
- return;
-
- if (message.content.startsWith("!verify")) {
- const email = message.content.split(" ")[1];
-
- if (!email) {
- return verificationChannel.send(
- "Please provide your email address. Usage: `!verify your_email@mail.dcu.ie`"
- );
- }
-
- const emailDomain = email.split("@")[1];
- if (!EMAIL_DOMAINS.includes(emailDomain)) {
- return verificationChannel.send(
- "You must use a valid DCU email address."
- );
- }
-
- const guild = client.guilds.cache.get(GUILD_ID);
- if (!guild) {
- console.error("Guild not found.");
- return;
- }
-
- const member = guild.members.cache.get(message.author.id);
- if (!member) {
- console.error("Member not found.");
- return;
- }
-
- const role = guild.roles.cache.find((r) => r.name === VERIFIED_ROLE_NAME);
- if (!role) {
- console.error("Role not found.");
- return;
- }
-
- if (member.roles.cache.has(role.id)) {
- return verificationChannel.send("You are already verified!");
- }
-
- const verificationCode = Math.floor(
- 100000 + Math.random() * 900000
- ).toString();
-
- const emailHtml = `
-
-
- Your Esports Verification Code
- Hi there,
- Thank you for requesting verification. Your Esports verification code is:
-
- ${verificationCode}
-
- This code is valid for 10 minutes. Please enter it in the verification Discord channel using the command !code your_code
.
- If you did not request this code, please ignore this email.
- Best regards,
Esports Committee
-
-
- `;
-
- try {
- await transporter.sendMail({
- from: `"${process.env.EMAIL_NAME}" <${process.env.EMAIL_USER}>`,
- to: email,
- subject: "Esports Verification Code",
- html: emailHtml, // Use HTML content
- });
-
- await VerificationCode.create({
- userId: message.author.id,
- email: email,
- code: verificationCode,
- });
-
- verificationChannel.send(
- `**A verification code has been sent to your email.**\n` +
- `Please reply with \`!code your_code\` to verify your account.\n` +
- `**Note:** The code is only valid for **10 minutes**.`
- );
- } catch (err) {
- console.error("Error sending email or saving verification code:", err);
- verificationChannel.send(
- "There was an error sending the verification email."
- );
- }
- }
-
- if (message.content.startsWith("!code")) {
- const code = message.content.split(" ")[1];
-
- if (!code) {
- return verificationChannel.send(
- "Please provide the verification code. Usage: `!code your_code`"
- );
- }
-
- try {
- const verificationEntry = await VerificationCode.findOne({
- userId: message.author.id,
- code,
- });
-
- if (!verificationEntry) {
- return verificationChannel.send(
- "Invalid or expired verification code. Please try again."
- );
- }
-
- const guild = client.guilds.cache.get(GUILD_ID);
- if (!guild) {
- console.error("Guild not found.");
- return;
- }
-
- const member = guild.members.cache.get(message.author.id);
- if (!member) {
- console.error("Member not found.");
- return;
- }
-
- const role = guild.roles.cache.find(
- (r) => r.name === VERIFIED_ROLE_NAME
- );
- if (!role) {
- console.error("Role not found.");
- return;
- }
-
- if (member.roles.cache.has(role.id)) {
- return verificationChannel.send("You are already verified!");
- }
-
- await member.roles.add(role);
- verificationChannel.send(
- `Congratulations ${message.author}, you have been verified!`
- );
-
- // No need to manually delete the verification entry, as it will expire automatically in 10 minutes
- } catch (err) {
- console.error("Error processing verification code:", err);
- verificationChannel.send(
- "There was an error processing your verification. Please try again later."
- );
- }
- }
- } catch (err) {
- console.error("Unhandled error in messageCreate event:", err);
- }
-});
-
-client.on("error", (err) => {
- console.error("Client error:", err);
-});
-
-client.login(process.env.BOT_TOKEN);
diff --git a/commands/code.js b/commands/code.js
new file mode 100644
index 0000000..440ff92
--- /dev/null
+++ b/commands/code.js
@@ -0,0 +1,95 @@
+const { SlashCommandBuilder } = require("discord.js");
+const VerificationCode = require("../models/VerificationCode");
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName("code")
+ .setDescription("Verify your account with a verification code")
+ .addStringOption((option) =>
+ option
+ .setName("code")
+ .setDescription("Your verification code")
+ .setRequired(true)
+ ),
+
+ async execute(interaction, client) {
+ const code = interaction.options.getString("code");
+
+ if (!code) {
+ return interaction.reply({
+ content: "Please provide the verification code.",
+ ephemeral: true,
+ });
+ }
+
+ try {
+ const verificationEntry = await VerificationCode.findOne({
+ userId: interaction.user.id,
+ code,
+ });
+
+ if (!verificationEntry) {
+ return interaction.reply({
+ content: "Invalid or expired verification code. Please try again.",
+ ephemeral: true,
+ });
+ }
+
+ const guild = client.guilds.cache.get(process.env.GUILD_ID);
+
+ if (!guild) {
+ console.error("Guild not found.");
+ return interaction.reply({
+ content: "Guild not found.",
+ ephemeral: true,
+ });
+ }
+
+ const member = guild.members.cache.get(interaction.user.id);
+
+ if (!member) {
+ console.error("Member not found in the guild.");
+ return interaction.reply({
+ content: "Member not found in the guild.",
+ ephemeral: true,
+ });
+ }
+
+ const role = guild.roles.cache.find(
+ (r) => r.name === process.env.VERIFIED_ROLE_NAME
+ );
+
+ if (!role) {
+ console.error(`Role "${process.env.VERIFIED_ROLE_NAME}" not found.`);
+ return interaction.reply({
+ content: `Role "${process.env.VERIFIED_ROLE_NAME}" not found.`,
+ ephemeral: true,
+ });
+ }
+
+ if (member.roles.cache.has(role.id)) {
+ return interaction.reply({
+ content: "You are already verified!",
+ ephemeral: true,
+ });
+ }
+
+ await member.roles.add(role);
+
+ interaction.reply({
+ content: `Congratulations ${interaction.user.username}, you have been verified!`,
+ ephemeral: true,
+ });
+
+ // the verification code is no longer needed, but it will automatically be deleted after 10 minutes
+ await VerificationCode.deleteOne({ userId: interaction.user.id, code });
+ } catch (err) {
+ console.error("Error processing verification code:", err);
+ interaction.reply({
+ content:
+ "There was an error processing your verification. Please try again later.",
+ ephemeral: true,
+ });
+ }
+ },
+};
diff --git a/commands/verify.js b/commands/verify.js
new file mode 100644
index 0000000..f313a59
--- /dev/null
+++ b/commands/verify.js
@@ -0,0 +1,121 @@
+const { SlashCommandBuilder } = require("discord.js");
+const nodemailer = require("nodemailer");
+const VerificationCode = require("../models/VerificationCode");
+
+const transporter = nodemailer.createTransport({
+ service: "Gmail",
+ auth: {
+ user: process.env.EMAIL_USER,
+ pass: process.env.EMAIL_PASS,
+ },
+});
+
+module.exports = {
+ data: new SlashCommandBuilder()
+ .setName("verify")
+ .setDescription("Verify your account with an email address")
+ .addStringOption((option) =>
+ option
+ .setName("email")
+ .setDescription("Your DCU email address")
+ .setRequired(true)
+ ),
+
+ async execute(interaction, client) {
+ const email = interaction.options.getString("email");
+ const emailDomain = email.split("@")[1];
+ const EMAIL_DOMAINS = process.env.EMAIL_DOMAINS.split(",");
+
+ if (!EMAIL_DOMAINS.includes(emailDomain)) {
+ return interaction.reply({
+ content: "You must use a valid DCU email address.",
+ ephemeral: true,
+ });
+ }
+
+ const guild = client.guilds.cache.get(process.env.GUILD_ID);
+
+ if (!guild) {
+ console.error("Guild not found.");
+ return interaction.reply({
+ content: "Guild not found.",
+ ephemeral: true,
+ });
+ }
+
+ const member = guild.members.cache.get(interaction.user.id);
+
+ if (!member) {
+ console.error("Member not found in the guild.");
+ return interaction.reply({
+ content: "Member not found in the guild.",
+ ephemeral: true,
+ });
+ }
+
+ const role = guild.roles.cache.find(
+ (r) => r.name === process.env.VERIFIED_ROLE_NAME
+ );
+
+ if (!role) {
+ console.error(`Role "${process.env.VERIFIED_ROLE_NAME}" not found.`);
+ return interaction.reply({
+ content: `Role "${process.env.VERIFIED_ROLE_NAME}" not found.`,
+ ephemeral: true,
+ });
+ }
+
+ if (member.roles.cache.has(role.id)) {
+ return interaction.reply({
+ content: "You are already verified!",
+ ephemeral: true,
+ });
+ }
+
+ const verificationCode = Math.floor(
+ 100000 + Math.random() * 900000
+ ).toString();
+
+ const emailHtml = `
+
+
+ Your Esports Verification Code
+ Hi there,
+ Your Esports verification code is:
+
+ ${verificationCode}
+
+ This code is valid for 10 minutes. Use it with the command /code your_code
.
+ If you did not request this code, please ignore this email.
+ Best regards,
Esports Committee
+
+
+ `;
+
+ try {
+ await transporter.sendMail({
+ from: `"${process.env.EMAIL_NAME}" <${process.env.EMAIL_USER}>`,
+ to: email,
+ subject: "Esports Verification Code",
+ html: emailHtml,
+ });
+
+ await VerificationCode.create({
+ userId: interaction.user.id,
+ email: email,
+ code: verificationCode,
+ });
+
+ interaction.reply({
+ content: `A verification code has been sent to your email. Use \`/code your_code\` to verify your account. The code is valid for 10 minutes.`,
+ ephemeral: true,
+ });
+ } catch (err) {
+ console.error("Error sending email or saving verification code:", err);
+ interaction.reply({
+ content: "There was an error sending the verification email.",
+ ephemeral: true,
+ });
+ }
+ },
+};
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..38c64be
--- /dev/null
+++ b/index.js
@@ -0,0 +1,79 @@
+require("dotenv").config();
+const {
+ Client,
+ GatewayIntentBits,
+ Collection,
+ REST,
+ Routes,
+} = require("discord.js");
+const mongoose = require("mongoose");
+const fs = require("fs");
+const path = require("path");
+
+const client = new Client({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent,
+ ],
+});
+
+const GUIlD_ID = process.env.GUILD_ID;
+
+client.commands = new Collection();
+
+const commandFiles = fs
+ .readdirSync(path.join(__dirname, "commands"))
+ .filter((file) => file.endsWith(".js"));
+
+for (const file of commandFiles) {
+ const command = require(`./commands/${file}`);
+ client.commands.set(command.data.name, command);
+}
+
+client.once("ready", async () => {
+ console.log(`Logges in as ${client.user.tag}`);
+
+ const commands = client.commands.map((cmd) => cmd.data.toJSON());
+
+ const rest = new REST({ version: "10" }).setToken(process.env.BOT_TOKEN);
+
+ try {
+ await rest.put(Routes.applicationGuildCommands(client.user.id, GUIlD_ID), {
+ body: commands,
+ });
+ console.log("Successfully registered application commands.");
+ } catch (err) {
+ console.error("Error registering application commands:", err);
+ }
+});
+
+// MongoDB connection
+mongoose
+ .connect(process.env.MONGODB_URI)
+ .then(() => console.log("Connected to MongoDB"))
+ .catch((err) => console.error("Failed to connect to MongoDB", err));
+
+client.on("interactionCreate", async (interaction) => {
+ if (!interaction.isCommand()) return;
+
+ const command = client.commands.get(interaction.commandName);
+
+ if (!command) return;
+
+ try {
+ await command.execute(interaction, client);
+ } catch (err) {
+ console.error(err);
+ await interaction.reply({
+ content: "There was an error while executing this command!",
+ ephemeral: true,
+ });
+ }
+});
+
+client.on("Error", (err) => {
+ console.error("Client error:", err);
+});
+
+client.login(process.env.BOT_TOKEN);