mirror of
https://github.com/aydenjahola/discord-multipurpose-bot.git
synced 2024-11-24 01:35:55 +00:00
rewrite the bot to use slash commands and use empheral messages for privacy
This commit is contained in:
parent
fb7c4a3d7e
commit
6ee0b874b5
6 changed files with 298 additions and 209 deletions
2
Procfile
2
Procfile
|
@ -1 +1 @@
|
|||
worker: node bot.js
|
||||
worker: node index.js
|
|
@ -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.
|
||||
|
|
206
bot.js
206
bot.js
|
@ -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 = `
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #333;">
|
||||
<h2 style="color: #1e90ff;">Your Esports Verification Code</h2>
|
||||
<p>Hi there,</p>
|
||||
<p>Thank you for requesting verification. Your Esports verification code is:</p>
|
||||
<h3 style="background-color: #f4f4f4; padding: 10px; border: 1px solid #ddd; border-radius: 5px; text-align: center; color: #1e90ff;">
|
||||
${verificationCode}
|
||||
</h3>
|
||||
<p>This code is valid for 10 minutes. Please enter it in the verification Discord channel using the command <code>!code your_code</code>.</p>
|
||||
<p>If you did not request this code, please ignore this email.</p>
|
||||
<p>Best regards,<br>Esports Committee</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
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);
|
95
commands/code.js
Normal file
95
commands/code.js
Normal file
|
@ -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,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
121
commands/verify.js
Normal file
121
commands/verify.js
Normal file
|
@ -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 = `
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; color: #333;">
|
||||
<h2 style="color: #1e90ff;">Your Esports Verification Code</h2>
|
||||
<p>Hi there,</p>
|
||||
<p>Your Esports verification code is:</p>
|
||||
<h3 style="background-color: #f4f4f4; padding: 10px; border: 1px solid #ddd; border-radius: 5px; text-align: center; color: #1e90ff;">
|
||||
${verificationCode}
|
||||
</h3>
|
||||
<p>This code is valid for 10 minutes. Use it with the command <code>/code your_code</code>.</p>
|
||||
<p>If you did not request this code, please ignore this email.</p>
|
||||
<p>Best regards,<br>Esports Committee</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
79
index.js
Normal file
79
index.js
Normal file
|
@ -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);
|
Loading…
Reference in a new issue