mirror of
https://github.com/aydenjahola/discord-multipurpose-bot.git
synced 2024-11-22 08:45:55 +00:00
Compare commits
No commits in common. "5a79b539b24bba5a1062787fe372257cff6dff00" and "45fdb3537f7dc9ce4137a4aab7f8e141c5da1d5f" have entirely different histories.
5a79b539b2
...
45fdb3537f
25 changed files with 48 additions and 1434 deletions
|
@ -11,23 +11,12 @@ module.exports = {
|
||||||
|
|
||||||
async execute(interaction, client) {
|
async execute(interaction, client) {
|
||||||
try {
|
try {
|
||||||
|
// Check if the user has the Manage Roles permission
|
||||||
const isMod = interaction.member.permissions.has(
|
const isMod = interaction.member.permissions.has(
|
||||||
PermissionsBitField.Flags.ManageRoles
|
PermissionsBitField.Flags.ManageRoles
|
||||||
);
|
);
|
||||||
|
|
||||||
const serverName = interaction.guild.name;
|
const serverName = interaction.guild.name;
|
||||||
const generalCommands = [];
|
|
||||||
const modCommands = [];
|
|
||||||
|
|
||||||
// Categorize commands
|
|
||||||
client.commands.forEach((command) => {
|
|
||||||
const commandLine = `/${command.data.name} - ${command.data.description}`;
|
|
||||||
if (!command.isModOnly) {
|
|
||||||
generalCommands.push(commandLine);
|
|
||||||
} else if (isMod) {
|
|
||||||
modCommands.push(`${commandLine} (Mods only)`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const helpEmbed = new EmbedBuilder()
|
const helpEmbed = new EmbedBuilder()
|
||||||
.setColor("#0099ff")
|
.setColor("#0099ff")
|
||||||
|
@ -41,46 +30,39 @@ module.exports = {
|
||||||
iconURL: client.user.displayAvatarURL(),
|
iconURL: client.user.displayAvatarURL(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Function to split commands into fields under 1024 characters
|
// Group commands into general and mod-only
|
||||||
const addCommandFields = (embed, commands, title) => {
|
const generalCommands = [];
|
||||||
let commandChunk = "";
|
const modCommands = [];
|
||||||
let chunkCount = 1;
|
|
||||||
|
|
||||||
commands.forEach((command) => {
|
client.commands.forEach((command) => {
|
||||||
// Check if adding this command will exceed the 1024 character limit
|
const commandLine = `/${command.data.name} - ${command.data.description}`;
|
||||||
if ((commandChunk + command).length > 1024) {
|
if (!command.isModOnly) {
|
||||||
// Add current chunk as a new field
|
generalCommands.push(commandLine);
|
||||||
embed.addFields({
|
} else if (isMod) {
|
||||||
name: `${title} (Part ${chunkCount})`,
|
modCommands.push(`${commandLine} (Mods only)`);
|
||||||
value: commandChunk,
|
|
||||||
});
|
|
||||||
commandChunk = ""; // Reset chunk for new field
|
|
||||||
chunkCount += 1;
|
|
||||||
}
|
|
||||||
// Append command to the current chunk
|
|
||||||
commandChunk += command + "\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add any remaining commands in the last chunk
|
|
||||||
if (commandChunk) {
|
|
||||||
embed.addFields({
|
|
||||||
name: `${title} (Part ${chunkCount})`,
|
|
||||||
value: commandChunk,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
// Add general commands in fields
|
helpEmbed.addFields({
|
||||||
if (generalCommands.length > 0) {
|
name: `General Commands (${generalCommands.length} available)`,
|
||||||
addCommandFields(helpEmbed, generalCommands, "General Commands");
|
value:
|
||||||
|
generalCommands.length > 0
|
||||||
|
? generalCommands.join("\n")
|
||||||
|
: "No general commands available.",
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isMod) {
|
||||||
|
helpEmbed.addFields({
|
||||||
|
name: `Mod-Only Commands (${modCommands.length} available)`,
|
||||||
|
value:
|
||||||
|
modCommands.length > 0
|
||||||
|
? modCommands.join("\n")
|
||||||
|
: "No mod-only commands available.",
|
||||||
|
inline: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add mod-only commands in fields, if user is a mod
|
|
||||||
if (isMod && modCommands.length > 0) {
|
|
||||||
addCommandFields(helpEmbed, modCommands, "Mod-Only Commands");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the single embed
|
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
embeds: [helpEmbed],
|
embeds: [helpEmbed],
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("invite")
|
|
||||||
.setDescription("Provides an invite link to add the bot to your server."),
|
|
||||||
|
|
||||||
async execute(interaction, client) {
|
|
||||||
try {
|
|
||||||
const botInviteLink = `https://discord.com/oauth2/authorize?client_id=${client.user.id}&permissions=8&scope=bot%20applications.commands`;
|
|
||||||
|
|
||||||
const inviteEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#0099ff")
|
|
||||||
.setTitle("Invite the Bot to Your Server!")
|
|
||||||
.setDescription(`**[Click here to invite the bot!](${botInviteLink})**`)
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: `Requested by ${interaction.user.tag}`,
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
});
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
embeds: [inviteEmbed],
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error executing the invite command:", error);
|
|
||||||
await interaction.reply({
|
|
||||||
content: "There was an error while executing this command!",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,113 +0,0 @@
|
||||||
const { SlashCommandBuilder } = require("discord.js");
|
|
||||||
const Event = require("../../models/Event");
|
|
||||||
const Participant = require("../../models/Participant");
|
|
||||||
const moment = require("moment");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("createevent")
|
|
||||||
.setDescription("Create a new event.")
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("name")
|
|
||||||
.setDescription("Name of the event")
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("description")
|
|
||||||
.setDescription("Description of the event")
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("category")
|
|
||||||
.setDescription("Category of the event")
|
|
||||||
.addChoices(
|
|
||||||
{ name: "Tournament", value: "tournament" },
|
|
||||||
{ name: "Meeting", value: "meeting" },
|
|
||||||
{ name: "Giveaway", value: "giveaway" },
|
|
||||||
{ name: "Other", value: "other" }
|
|
||||||
)
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("location")
|
|
||||||
.setDescription("Location of the event (default is Online)")
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("startdatetime")
|
|
||||||
.setDescription("Start date and time of the event (YYYY-MM-DD HH:mm)")
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("enddatetime")
|
|
||||||
.setDescription("End date and time of the event (YYYY-MM-DD HH:mm)")
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("recurrence")
|
|
||||||
.setDescription("Recurrence of the event")
|
|
||||||
.addChoices(
|
|
||||||
{ name: "None", value: "none" },
|
|
||||||
{ name: "Daily", value: "daily" },
|
|
||||||
{ name: "Weekly", value: "weekly" },
|
|
||||||
{ name: "Monthly", value: "monthly" }
|
|
||||||
)
|
|
||||||
),
|
|
||||||
async execute(interaction) {
|
|
||||||
const name = interaction.options.getString("name");
|
|
||||||
const description = interaction.options.getString("description");
|
|
||||||
const category = interaction.options.getString("category");
|
|
||||||
const location = interaction.options.getString("location") || "Online";
|
|
||||||
const startDate = moment(
|
|
||||||
interaction.options.getString("startdatetime"),
|
|
||||||
"YYYY-MM-DD HH:mm"
|
|
||||||
);
|
|
||||||
const endDate = moment(
|
|
||||||
interaction.options.getString("enddatetime"),
|
|
||||||
"YYYY-MM-DD HH:mm"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!startDate.isValid() || !endDate.isValid()) {
|
|
||||||
return await interaction.reply(
|
|
||||||
"Invalid date format! Use YYYY-MM-DD HH:mm."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startDate.isAfter(endDate)) {
|
|
||||||
return await interaction.reply("Start date must be before the end date.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new event
|
|
||||||
const event = new Event({
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
category,
|
|
||||||
location,
|
|
||||||
startDate: startDate.toISOString(),
|
|
||||||
endDate: endDate.toISOString(),
|
|
||||||
organizerId: interaction.user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await event.save();
|
|
||||||
await interaction.reply(
|
|
||||||
`Event "${name}" created! Starts at ${startDate.format(
|
|
||||||
"MMMM Do YYYY, h:mm a"
|
|
||||||
)} and ends at ${endDate.format("MMMM Do YYYY, h:mm a")}.`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (error.code === 11000) {
|
|
||||||
await interaction.reply("An event with that name already exists.");
|
|
||||||
} else {
|
|
||||||
await interaction.reply("Error creating event. Please try again.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,150 +0,0 @@
|
||||||
// commands/editevent.js
|
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
const Event = require("../../models/Event");
|
|
||||||
const moment = require("moment");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("editevent")
|
|
||||||
.setDescription("Edit an existing event.")
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("name")
|
|
||||||
.setDescription("Name of the event to edit")
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("description")
|
|
||||||
.setDescription("New description of the event")
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("category")
|
|
||||||
.setDescription("New category of the event")
|
|
||||||
.addChoices(
|
|
||||||
{ name: "Tournament", value: "tournament" },
|
|
||||||
{ name: "Meeting", value: "meeting" },
|
|
||||||
{ name: "Giveaway", value: "giveaway" },
|
|
||||||
{ name: "Other", value: "other" }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option.setName("location").setDescription("New location of the event")
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("startdatetime")
|
|
||||||
.setDescription(
|
|
||||||
"New start date and time of the event (YYYY-MM-DD HH:mm)"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("enddatetime")
|
|
||||||
.setDescription("New end date and time of the event (YYYY-MM-DD HH:mm)")
|
|
||||||
)
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("recurrence")
|
|
||||||
.setDescription("New recurrence of the event")
|
|
||||||
.addChoices(
|
|
||||||
{ name: "None", value: "none" },
|
|
||||||
{ name: "Daily", value: "daily" },
|
|
||||||
{ name: "Weekly", value: "weekly" },
|
|
||||||
{ name: "Monthly", value: "monthly" }
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const name = interaction.options.getString("name");
|
|
||||||
const event = await Event.findOne({ name });
|
|
||||||
|
|
||||||
if (!event) {
|
|
||||||
return await interaction.reply("Event not found!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interaction.options.getString("description")) {
|
|
||||||
event.description = interaction.options.getString("description");
|
|
||||||
}
|
|
||||||
if (interaction.options.getString("category")) {
|
|
||||||
event.category = interaction.options.getString("category");
|
|
||||||
}
|
|
||||||
if (interaction.options.getString("location")) {
|
|
||||||
event.location = interaction.options.getString("location");
|
|
||||||
}
|
|
||||||
if (interaction.options.getString("startdatetime")) {
|
|
||||||
const newStartDate = moment(
|
|
||||||
interaction.options.getString("startdatetime"),
|
|
||||||
"YYYY-MM-DD HH:mm"
|
|
||||||
);
|
|
||||||
if (!newStartDate.isValid()) {
|
|
||||||
return await interaction.reply(
|
|
||||||
"Invalid start date format! Use YYYY-MM-DD HH:mm."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
event.startDate = newStartDate.toISOString();
|
|
||||||
}
|
|
||||||
if (interaction.options.getString("enddatetime")) {
|
|
||||||
const newEndDate = moment(
|
|
||||||
interaction.options.getString("enddatetime"),
|
|
||||||
"YYYY-MM-DD HH:mm"
|
|
||||||
);
|
|
||||||
if (!newEndDate.isValid()) {
|
|
||||||
return await interaction.reply(
|
|
||||||
"Invalid end date format! Use YYYY-MM-DD HH:mm."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (moment(event.startDate).isAfter(newEndDate)) {
|
|
||||||
return await interaction.reply(
|
|
||||||
"End date must be after the start date."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
event.endDate = newEndDate.toISOString();
|
|
||||||
}
|
|
||||||
if (interaction.options.getString("recurrence")) {
|
|
||||||
event.recurrence = interaction.options.getString("recurrence");
|
|
||||||
}
|
|
||||||
|
|
||||||
await event.save();
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle(`Event "${event.name}" Updated Successfully`)
|
|
||||||
.setColor("#00FF00")
|
|
||||||
.addFields(
|
|
||||||
{
|
|
||||||
name: "Description",
|
|
||||||
value: event.description || "No description provided",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Category",
|
|
||||||
value: event.category || "Not specified",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Location",
|
|
||||||
value: event.location || "Not specified",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Start Date",
|
|
||||||
value: moment(event.startDate).format("YYYY-MM-DD HH:mm"),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "End Date",
|
|
||||||
value: moment(event.endDate).format("YYYY-MM-DD HH:mm"),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{ name: "Recurrence", value: event.recurrence || "None", inline: true }
|
|
||||||
)
|
|
||||||
.setFooter({
|
|
||||||
text: "Event updated successfully",
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
})
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed] });
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,92 +0,0 @@
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
const Event = require("../../models/Event");
|
|
||||||
const Participant = require("../../models/Participant");
|
|
||||||
const moment = require("moment");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("joinevent")
|
|
||||||
.setDescription("Join an event.")
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("event_name")
|
|
||||||
.setDescription("Name of the event to join")
|
|
||||||
.setRequired(true)
|
|
||||||
),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const eventName = interaction.options.getString("event_name");
|
|
||||||
const event = await Event.findOne({ name: eventName });
|
|
||||||
|
|
||||||
if (!event) {
|
|
||||||
return interaction.reply({
|
|
||||||
content: `Event "${eventName}" not found.`,
|
|
||||||
ephemeral: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let participant = await Participant.findOne({
|
|
||||||
userId: interaction.user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!participant) {
|
|
||||||
participant = new Participant({
|
|
||||||
userId: interaction.user.id,
|
|
||||||
username: interaction.user.username,
|
|
||||||
});
|
|
||||||
await participant.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!event.participants.includes(participant._id)) {
|
|
||||||
event.participants.push(participant._id);
|
|
||||||
await event.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle(`Successfully Joined Event: ${event.name}`)
|
|
||||||
.setColor("#3498db")
|
|
||||||
.addFields(
|
|
||||||
{
|
|
||||||
name: "Category",
|
|
||||||
value: event.category ? String(event.category) : "Not specified",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Location",
|
|
||||||
value: event.location ? String(event.location) : "Virtual",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Start Date",
|
|
||||||
value: moment(event.startDate).isValid()
|
|
||||||
? moment(event.startDate).format("YYYY-MM-DD HH:mm")
|
|
||||||
: "N/A",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "End Date",
|
|
||||||
value: moment(event.endDate).isValid()
|
|
||||||
? moment(event.endDate).format("YYYY-MM-DD HH:mm")
|
|
||||||
: "N/A",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Recurrence",
|
|
||||||
value: event.recurrence ? String(event.recurrence) : "None",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Participants",
|
|
||||||
value: String(event.participants.length),
|
|
||||||
inline: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: `Joined by ${interaction.user.username}`,
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
});
|
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed] });
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,72 +0,0 @@
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
const Event = require("../../models/Event");
|
|
||||||
const Participant = require("../../models/Participant");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("leaveevent")
|
|
||||||
.setDescription("Leave an event.")
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("event_name")
|
|
||||||
.setDescription("Name of the event to leave")
|
|
||||||
.setRequired(true)
|
|
||||||
),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const eventName = interaction.options.getString("event_name");
|
|
||||||
const event = await Event.findOne({ name: eventName });
|
|
||||||
|
|
||||||
if (!event) {
|
|
||||||
return await interaction.reply({
|
|
||||||
content: `Event "${eventName}" not found.`,
|
|
||||||
ephemeral: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const participant = await Participant.findOne({
|
|
||||||
userId: interaction.user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!participant) {
|
|
||||||
return await interaction.reply({
|
|
||||||
content: `You are not a participant of this event.`,
|
|
||||||
ephemeral: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
event.participants.pull(participant._id);
|
|
||||||
await event.save();
|
|
||||||
|
|
||||||
const isInOtherEvents = await Event.exists({
|
|
||||||
participants: participant._id,
|
|
||||||
});
|
|
||||||
if (!isInOtherEvents) {
|
|
||||||
await Participant.deleteOne({ userId: interaction.user.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle(`Left Event: ${event.name}`)
|
|
||||||
.setColor("#e74c3c")
|
|
||||||
.addFields(
|
|
||||||
{
|
|
||||||
name: "Category",
|
|
||||||
value: event.category || "Not specified",
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{ name: "Location", value: event.location || "Virtual", inline: true },
|
|
||||||
{
|
|
||||||
name: "Participants Remaining",
|
|
||||||
value: String(event.participants.length),
|
|
||||||
inline: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.setFooter({
|
|
||||||
text: `Left by ${interaction.user.username}`,
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
})
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed] });
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,51 +0,0 @@
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
const Event = require("../../models/Event");
|
|
||||||
const moment = require("moment");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("listevents")
|
|
||||||
.setDescription("List all upcoming events."),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const { user } = interaction;
|
|
||||||
const upcomingEvents = await Event.find({ status: "upcoming" }).populate(
|
|
||||||
"participants"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (upcomingEvents.length === 0) {
|
|
||||||
return interaction.reply("There are no upcoming events.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle("Upcoming Events")
|
|
||||||
.setColor("#00FF00")
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: `Requested by ${user.username}`,
|
|
||||||
iconURL: user.displayAvatarURL(),
|
|
||||||
});
|
|
||||||
|
|
||||||
upcomingEvents.forEach((event) => {
|
|
||||||
const participantsList =
|
|
||||||
event.participants.map((p) => p.username).join(", ") ||
|
|
||||||
"No Particiapnts yet";
|
|
||||||
|
|
||||||
embed.addFields({
|
|
||||||
name: event.name,
|
|
||||||
value: `**Description:** ${event.description}\n**Category:** ${
|
|
||||||
event.category
|
|
||||||
}\n**Location:** ${event.location}\n**Start Date:** ${moment(
|
|
||||||
event.startDate
|
|
||||||
).format("YYYY-MM-DD HH:mm")}\n**End Date:** ${moment(
|
|
||||||
event.endDate
|
|
||||||
).format("YYYY-MM-DD HH:mm")}\n**Recurrence:** ${
|
|
||||||
event.recurrence
|
|
||||||
}\n**Participants:** ${participantsList}`,
|
|
||||||
inline: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed] });
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,12 +0,0 @@
|
||||||
const { SlashCommandBuilder } = require("discord.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("coinflip")
|
|
||||||
.setDescription("Flip a coin!"),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const result = Math.random() < 0.5 ? "Heads" : "Tails";
|
|
||||||
await interaction.reply(`🪙 It's ${result}!`);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,23 +0,0 @@
|
||||||
const { SlashCommandBuilder } = require("discord.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("roll")
|
|
||||||
.setDescription("Roll a dice!")
|
|
||||||
.addIntegerOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("sides")
|
|
||||||
.setDescription("Number of sides on the dice")
|
|
||||||
.setRequired(false)
|
|
||||||
.setMinValue(2)
|
|
||||||
.setMaxValue(100)
|
|
||||||
),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const sides = interaction.options.getInteger("sides") || 6;
|
|
||||||
const result = Math.floor(Math.random() * sides) + 1;
|
|
||||||
await interaction.reply(
|
|
||||||
`🎲 You rolled a ${result} on a ${sides}-sided dice!`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,41 +0,0 @@
|
||||||
const { SlashCommandBuilder } = require("discord.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("rps")
|
|
||||||
.setDescription("Play Rock Paper Scissors!")
|
|
||||||
.addStringOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("choice")
|
|
||||||
.setDescription("Choose rock, paper, or scissors")
|
|
||||||
.setRequired(true)
|
|
||||||
.addChoices(
|
|
||||||
{ name: "Rock", value: "rock" },
|
|
||||||
{ name: "Paper", value: "paper" },
|
|
||||||
{ name: "Scissors", value: "scissors" }
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const userChoice = interaction.options.getString("choice");
|
|
||||||
const choices = ["rock", "paper", "scissors"];
|
|
||||||
const botChoice = choices[Math.floor(Math.random() * choices.length)];
|
|
||||||
|
|
||||||
let result;
|
|
||||||
if (userChoice === botChoice) {
|
|
||||||
result = "It's a draw!";
|
|
||||||
} else if (
|
|
||||||
(userChoice === "rock" && botChoice === "scissors") ||
|
|
||||||
(userChoice === "paper" && botChoice === "rock") ||
|
|
||||||
(userChoice === "scissors" && botChoice === "paper")
|
|
||||||
) {
|
|
||||||
result = "You win!";
|
|
||||||
} else {
|
|
||||||
result = "You lose!";
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.reply(
|
|
||||||
`You chose ${userChoice}. I chose ${botChoice}. ${result}`
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,401 +0,0 @@
|
||||||
const {
|
|
||||||
SlashCommandBuilder,
|
|
||||||
EmbedBuilder,
|
|
||||||
ButtonBuilder,
|
|
||||||
ActionRowBuilder,
|
|
||||||
ButtonStyle,
|
|
||||||
} = require("discord.js");
|
|
||||||
const SpyfallGame = require("../../models/SpyfallGame");
|
|
||||||
const SpyfallLocation = require("../../models/SpyfallLocation");
|
|
||||||
const { v4: uuidv4 } = require("uuid");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("spyfall")
|
|
||||||
.setDescription("Start a game of Spyfall."),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
const guildId = interaction.guild.id;
|
|
||||||
const gameId = uuidv4();
|
|
||||||
|
|
||||||
const existingGame = await SpyfallGame.findOne({ gameId });
|
|
||||||
if (existingGame && existingGame.status === "ongoing") {
|
|
||||||
return interaction.reply(
|
|
||||||
"A game is already in progress! Please finish the current game first."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const locations = await SpyfallLocation.find({});
|
|
||||||
|
|
||||||
if (!locations || locations.length === 0) {
|
|
||||||
return interaction.reply("No locations found. Please try again later.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedLocation =
|
|
||||||
locations[Math.floor(Math.random() * locations.length)].name;
|
|
||||||
|
|
||||||
const players = new Set();
|
|
||||||
|
|
||||||
const joinEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#ffcc00")
|
|
||||||
.setTitle("Join the Spyfall Game!")
|
|
||||||
.setDescription("Click the button below to join the game.")
|
|
||||||
.addFields({ name: "Current Players:", value: "None yet." })
|
|
||||||
.setFooter({ text: "You need at least 2 players to start!" })
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
const joinButton = new ButtonBuilder()
|
|
||||||
.setCustomId("join_game")
|
|
||||||
.setLabel("Join Game")
|
|
||||||
.setStyle(ButtonStyle.Success);
|
|
||||||
|
|
||||||
const leaveButton = new ButtonBuilder()
|
|
||||||
.setCustomId("leave_game")
|
|
||||||
.setLabel("Leave Game")
|
|
||||||
.setStyle(ButtonStyle.Danger);
|
|
||||||
|
|
||||||
const startButton = new ButtonBuilder()
|
|
||||||
.setCustomId("start_game")
|
|
||||||
.setLabel("Start Game")
|
|
||||||
.setStyle(ButtonStyle.Primary)
|
|
||||||
.setDisabled(true);
|
|
||||||
|
|
||||||
const buttonRow = new ActionRowBuilder().addComponents(
|
|
||||||
joinButton,
|
|
||||||
leaveButton,
|
|
||||||
startButton
|
|
||||||
);
|
|
||||||
|
|
||||||
const joinMessage = await interaction.reply({
|
|
||||||
embeds: [joinEmbed],
|
|
||||||
components: [buttonRow],
|
|
||||||
fetchReply: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const filter = (i) =>
|
|
||||||
i.customId === "join_game" ||
|
|
||||||
i.customId === "leave_game" ||
|
|
||||||
i.customId === "start_game";
|
|
||||||
|
|
||||||
const collector = joinMessage.createMessageComponentCollector({ filter });
|
|
||||||
|
|
||||||
collector.on("collect", async (i) => {
|
|
||||||
try {
|
|
||||||
if (!players.has(i.user.id) && i.customId !== "join_game") {
|
|
||||||
return i.reply({
|
|
||||||
content: "You cannot interact with this button.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i.customId === "join_game") {
|
|
||||||
if (players.has(i.user.id)) {
|
|
||||||
return i.reply({
|
|
||||||
content: "You are already in the game!",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
players.add(i.user.id);
|
|
||||||
const currentPlayers =
|
|
||||||
Array.from(players)
|
|
||||||
.map(
|
|
||||||
(id) => interaction.guild.members.cache.get(id).user.username
|
|
||||||
)
|
|
||||||
.join(", ") || "None yet.";
|
|
||||||
|
|
||||||
joinEmbed
|
|
||||||
.setDescription("Click the button below to join the game.")
|
|
||||||
.setFields([{ name: "Current Players:", value: currentPlayers }]);
|
|
||||||
|
|
||||||
if (players.size >= 2) {
|
|
||||||
startButton.setDisabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await i.update({ embeds: [joinEmbed], components: [buttonRow] });
|
|
||||||
} else if (i.customId === "leave_game") {
|
|
||||||
if (players.has(i.user.id)) {
|
|
||||||
players.delete(i.user.id);
|
|
||||||
const currentPlayers =
|
|
||||||
Array.from(players)
|
|
||||||
.map(
|
|
||||||
(id) =>
|
|
||||||
interaction.guild.members.cache.get(id).user.username
|
|
||||||
)
|
|
||||||
.join(", ") || "None yet.";
|
|
||||||
|
|
||||||
joinEmbed
|
|
||||||
.setDescription("Click the button below to join the game.")
|
|
||||||
.setFields([
|
|
||||||
{ name: "Current Players:", value: currentPlayers },
|
|
||||||
]);
|
|
||||||
await i.update({ embeds: [joinEmbed], components: [buttonRow] });
|
|
||||||
} else {
|
|
||||||
await i.reply({
|
|
||||||
content: "You are not part of the game.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (i.customId === "start_game") {
|
|
||||||
if (players.size < 2) {
|
|
||||||
return i.reply({
|
|
||||||
content: "You need at least 2 players to start the game.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const spyIndex = Math.floor(Math.random() * players.size);
|
|
||||||
const spy = Array.from(players)[spyIndex];
|
|
||||||
|
|
||||||
const newGame = new SpyfallGame({
|
|
||||||
gameId,
|
|
||||||
guildId,
|
|
||||||
location: selectedLocation,
|
|
||||||
spy,
|
|
||||||
players: Array.from(players),
|
|
||||||
status: "ongoing",
|
|
||||||
});
|
|
||||||
|
|
||||||
await newGame.save();
|
|
||||||
|
|
||||||
const locationMessage = `The game has started! The location is **${selectedLocation}**.`;
|
|
||||||
|
|
||||||
for (const playerId of players) {
|
|
||||||
const player = await interaction.guild.members.cache.get(playerId)
|
|
||||||
.user;
|
|
||||||
|
|
||||||
if (playerId !== spy) {
|
|
||||||
await player.send(locationMessage);
|
|
||||||
} else {
|
|
||||||
await player.send(
|
|
||||||
"The game has started! You are the spy! Try to blend in!"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setColor("#ffcc00")
|
|
||||||
.setTitle("Spyfall Game Started!")
|
|
||||||
.setDescription("The game has started! One of you is the spy!")
|
|
||||||
.setFooter({
|
|
||||||
text: "The spy must figure out the location without revealing themselves!",
|
|
||||||
})
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
await interaction.followUp({ embeds: [embed], components: [] });
|
|
||||||
collector.stop();
|
|
||||||
await interaction.channel.send({
|
|
||||||
content: "The game has started! Use `/stopspyfall` to end it.",
|
|
||||||
});
|
|
||||||
|
|
||||||
await startQuestioningPhase(
|
|
||||||
interaction,
|
|
||||||
spy,
|
|
||||||
players,
|
|
||||||
selectedLocation,
|
|
||||||
gameId
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error during button interaction:", error);
|
|
||||||
await i.reply({
|
|
||||||
content:
|
|
||||||
"There was an error processing your request. Please try again later.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching locations or starting the game:", error);
|
|
||||||
await interaction.reply(
|
|
||||||
"There was an error fetching locations or starting the game. Please try again later."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
async function startQuestioningPhase(
|
|
||||||
interaction,
|
|
||||||
spy,
|
|
||||||
players,
|
|
||||||
selectedLocation,
|
|
||||||
gameId
|
|
||||||
) {
|
|
||||||
const ongoingGame = await SpyfallGame.findOne({ gameId });
|
|
||||||
|
|
||||||
if (!ongoingGame || ongoingGame.status !== "ongoing") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const playerArray = Array.from(players);
|
|
||||||
let currentIndex = 0;
|
|
||||||
|
|
||||||
async function askQuestion() {
|
|
||||||
if (currentIndex >= playerArray.length) {
|
|
||||||
return startVotingPhase(interaction, spy, playerArray, gameId);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPlayerId = playerArray[currentIndex];
|
|
||||||
const currentPlayer =
|
|
||||||
interaction.guild.members.cache.get(currentPlayerId).user;
|
|
||||||
|
|
||||||
const questionEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#ffcc00")
|
|
||||||
.setTitle("Your Turn to Ask a Question!")
|
|
||||||
.setDescription(
|
|
||||||
`It's **${currentPlayer.username}**'s turn to ask a question about the location.`
|
|
||||||
)
|
|
||||||
.setFooter({ text: "Click 'Finish Turn' when you're done." });
|
|
||||||
|
|
||||||
const finishButton = new ButtonBuilder()
|
|
||||||
.setCustomId(`finish_turn_${currentPlayerId}`)
|
|
||||||
.setLabel("Finish Turn")
|
|
||||||
.setStyle(ButtonStyle.Primary);
|
|
||||||
|
|
||||||
const buttonRow = new ActionRowBuilder().addComponents(finishButton);
|
|
||||||
|
|
||||||
await interaction.channel.send({
|
|
||||||
embeds: [questionEmbed],
|
|
||||||
components: [buttonRow],
|
|
||||||
});
|
|
||||||
|
|
||||||
const filter = (i) =>
|
|
||||||
i.customId === `finish_turn_${currentPlayerId}` &&
|
|
||||||
i.user.id === currentPlayerId;
|
|
||||||
const collector = interaction.channel.createMessageComponentCollector({
|
|
||||||
filter,
|
|
||||||
time: 30000,
|
|
||||||
});
|
|
||||||
|
|
||||||
collector.on("collect", async (i) => {
|
|
||||||
await i.reply({ content: "Your turn has ended!", ephemeral: true });
|
|
||||||
collector.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
collector.on("end", async () => {
|
|
||||||
currentIndex++;
|
|
||||||
await askQuestion();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
askQuestion();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startVotingPhase(
|
|
||||||
interaction,
|
|
||||||
spy,
|
|
||||||
players,
|
|
||||||
gameId,
|
|
||||||
selectedLocation
|
|
||||||
) {
|
|
||||||
// Ensure players is an array
|
|
||||||
if (!Array.isArray(players)) {
|
|
||||||
console.error("Players is not an array:", players);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ongoingGame = await SpyfallGame.findOne({ gameId });
|
|
||||||
|
|
||||||
if (!ongoingGame || ongoingGame.status !== "ongoing") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const votes = new Map();
|
|
||||||
const voteEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#ffcc00")
|
|
||||||
.setTitle("Voting Phase")
|
|
||||||
.setDescription(
|
|
||||||
"Vote for who you think the spy is! (You cannot vote for yourself)"
|
|
||||||
)
|
|
||||||
.setFooter({ text: "Click the buttons below to vote." });
|
|
||||||
|
|
||||||
const voteButtons = players.map((playerId) =>
|
|
||||||
new ButtonBuilder()
|
|
||||||
.setCustomId(`vote_${playerId}`)
|
|
||||||
.setLabel(interaction.guild.members.cache.get(playerId).user.username)
|
|
||||||
.setStyle(ButtonStyle.Primary)
|
|
||||||
);
|
|
||||||
|
|
||||||
const voteRow = new ActionRowBuilder().addComponents(voteButtons);
|
|
||||||
const voteMessage = await interaction.channel.send({
|
|
||||||
embeds: [voteEmbed],
|
|
||||||
components: [voteRow],
|
|
||||||
});
|
|
||||||
|
|
||||||
const voteFilter = (i) =>
|
|
||||||
i.customId.startsWith("vote_") && players.includes(i.user.id);
|
|
||||||
const voteCollector = voteMessage.createMessageComponentCollector({
|
|
||||||
filter: voteFilter,
|
|
||||||
});
|
|
||||||
|
|
||||||
voteCollector.on("collect", async (i) => {
|
|
||||||
const votedPlayerId = i.customId.split("_")[1];
|
|
||||||
|
|
||||||
if (i.user.id === votedPlayerId) {
|
|
||||||
await i.reply({
|
|
||||||
content: "You cannot vote for yourself.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the vote
|
|
||||||
votes.set(votedPlayerId, (votes.get(votedPlayerId) || 0) + 1);
|
|
||||||
|
|
||||||
await i.reply({
|
|
||||||
content: `You voted for ${
|
|
||||||
interaction.guild.members.cache.get(votedPlayerId).user.username
|
|
||||||
}.`,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (votes.size === players.length) {
|
|
||||||
voteCollector.stop();
|
|
||||||
await revealSpy(interaction, spy, votes, selectedLocation, gameId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
voteCollector.on("end", async () => {
|
|
||||||
if (votes.size > 0) {
|
|
||||||
await revealSpy(interaction, spy, votes, selectedLocation, gameId);
|
|
||||||
} else {
|
|
||||||
await interaction.channel.send("Voting timed out! No votes were cast.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function revealSpy(interaction, spy, votes, selectedLocation, gameId) {
|
|
||||||
const voteEntries = Array.from(votes.entries());
|
|
||||||
const highestVote = Math.max(...voteEntries.map(([_, v]) => v));
|
|
||||||
const suspectedSpy = voteEntries.find(([_, v]) => v === highestVote)[0];
|
|
||||||
|
|
||||||
const resultEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#ffcc00")
|
|
||||||
.setTitle("Voting Results")
|
|
||||||
.addFields(
|
|
||||||
{
|
|
||||||
name: "Suspected Spy:",
|
|
||||||
value: interaction.guild.members.cache.get(suspectedSpy).user.username,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Actual Spy:",
|
|
||||||
value: interaction.guild.members.cache.get(spy).user.username,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.setFooter({
|
|
||||||
text:
|
|
||||||
suspectedSpy === spy
|
|
||||||
? "The spy has been caught!"
|
|
||||||
: "The spy has escaped!",
|
|
||||||
})
|
|
||||||
.setTimestamp();
|
|
||||||
|
|
||||||
const result = await SpyfallGame.updateOne({ gameId }, { status: "ended" });
|
|
||||||
if (result.modifiedCount === 0) {
|
|
||||||
console.log("No game found or status was already 'ended'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.channel.send({ embeds: [resultEmbed] });
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
const { SlashCommandBuilder } = require("discord.js");
|
|
||||||
const SpyfallGame = require("../../models/SpyfallGame");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("stopspyfall")
|
|
||||||
.setDescription("Stop the current Spyfall game in this server."),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
try {
|
|
||||||
const guildId = interaction.guild.id;
|
|
||||||
|
|
||||||
const ongoingGame = await SpyfallGame.findOne({
|
|
||||||
guildId: guildId,
|
|
||||||
status: "ongoing",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!ongoingGame) {
|
|
||||||
return interaction.reply({
|
|
||||||
content: "No Spyfall game is currently in progress in this server!",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ongoingGame.status = "ended";
|
|
||||||
await ongoingGame.save();
|
|
||||||
|
|
||||||
await interaction.reply("The Spyfall game has been stopped!");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error stopping the game:", error);
|
|
||||||
await interaction.reply({
|
|
||||||
content:
|
|
||||||
"There was an error trying to stop the game. Please try again later.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,61 +0,0 @@
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
const axios = require("axios");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("thisdayinhistory")
|
|
||||||
.setDescription("Shows historical events that happened on this day."),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
try {
|
|
||||||
const today = new Date();
|
|
||||||
const month = today.getMonth() + 1;
|
|
||||||
const day = today.getDate();
|
|
||||||
|
|
||||||
const response = await axios.get(
|
|
||||||
`https://en.wikipedia.org/api/rest_v1/feed/onthisday/events/${month}/${day}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.data.events.length === 0) {
|
|
||||||
return interaction.reply("No significant events found for today.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const events = response.data.events
|
|
||||||
.map((event) => `${event.year}: ${event.text}`)
|
|
||||||
.join("\n");
|
|
||||||
|
|
||||||
const maxDescriptionLength = 4096;
|
|
||||||
const truncatedEvents =
|
|
||||||
events.length > maxDescriptionLength
|
|
||||||
? events.slice(0, maxDescriptionLength - 3) + "..."
|
|
||||||
: events;
|
|
||||||
|
|
||||||
const historyEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#0099ff")
|
|
||||||
.setTitle(`This Day in History: ${month}/${day}`)
|
|
||||||
.setDescription(truncatedEvents)
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: `Requested by ${interaction.user.tag}`,
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
});
|
|
||||||
|
|
||||||
await interaction.reply({ embeds: [historyEmbed] });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error executing the thisdayinhistory command:", error);
|
|
||||||
|
|
||||||
if (error.response) {
|
|
||||||
await interaction.reply({
|
|
||||||
content: `API returned an error: ${error.response.status} - ${error.response.data.title}`,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await interaction.reply({
|
|
||||||
content:
|
|
||||||
"There was an error while executing this command! Please try again later.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,64 +0,0 @@
|
||||||
const {
|
|
||||||
SlashCommandBuilder,
|
|
||||||
EmbedBuilder,
|
|
||||||
PermissionsBitField,
|
|
||||||
} = require("discord.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("servers")
|
|
||||||
.setDescription("Displays a list of servers the bot is currently in"),
|
|
||||||
isModOnly: true,
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
try {
|
|
||||||
// Check if the user has the Manage Server permission
|
|
||||||
if (
|
|
||||||
!interaction.member.permissions.has(
|
|
||||||
PermissionsBitField.Flags.ManageGuild
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
await interaction.reply({
|
|
||||||
content: "You do not have permission to use this command!",
|
|
||||||
ephemeral: false,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const guilds = interaction.client.guilds.cache.map((guild) => ({
|
|
||||||
name: guild.name,
|
|
||||||
memberCount: guild.memberCount,
|
|
||||||
id: guild.id,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const serversEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#0099ff")
|
|
||||||
.setTitle("Servers the Bot is In")
|
|
||||||
.setDescription(`Currently in ${guilds.length} servers`)
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: `Requested by ${interaction.user.tag}`,
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
});
|
|
||||||
|
|
||||||
guilds.forEach((guild) => {
|
|
||||||
serversEmbed.addFields({
|
|
||||||
name: guild.name,
|
|
||||||
value: `ID: ${guild.id}\nMembers: ${guild.memberCount}`,
|
|
||||||
inline: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
embeds: [serversEmbed],
|
|
||||||
ephemeral: false,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error executing servers command:", error);
|
|
||||||
await interaction.reply({
|
|
||||||
content: "There was an error while executing this command!",
|
|
||||||
ephemeral: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,5 +1,4 @@
|
||||||
const { SlashCommandBuilder } = require("discord.js");
|
const { SlashCommandBuilder } = require("discord.js");
|
||||||
const ServerSettings = require("../../models/ServerSettings");
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder()
|
||||||
|
@ -17,23 +16,11 @@ module.exports = {
|
||||||
),
|
),
|
||||||
|
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
const serverSettings = await ServerSettings.findOne({
|
// Check if the command is used in the allowed channel
|
||||||
guildId: interaction.guild.id,
|
const allowedChannelId = "1299134735330836521";
|
||||||
});
|
if (interaction.channelId !== allowedChannelId) {
|
||||||
|
|
||||||
if (!serverSettings) {
|
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content:
|
content: `This command can only be used in the designated channel.`,
|
||||||
"Server settings are not configured. Please run the setup command.",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionItemChannelId = serverSettings.actionItemsChannelId;
|
|
||||||
|
|
||||||
if (interaction.channelId !== actionItemChannelId) {
|
|
||||||
return interaction.reply({
|
|
||||||
content: `This command can only be used in the <#${actionItemChannelId}> channel.`,
|
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,6 @@ module.exports = {
|
||||||
.setName("emaildomains")
|
.setName("emaildomains")
|
||||||
.setDescription("Comma-separated list of allowed email domains.")
|
.setDescription("Comma-separated list of allowed email domains.")
|
||||||
.setRequired(true)
|
.setRequired(true)
|
||||||
)
|
|
||||||
.addChannelOption((option) =>
|
|
||||||
option
|
|
||||||
.setName("actionitemschannel")
|
|
||||||
.setDescription(
|
|
||||||
"Select the allowed channel for action items. (Optional)"
|
|
||||||
)
|
|
||||||
.setRequired(false)
|
|
||||||
),
|
),
|
||||||
|
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
|
@ -63,10 +55,7 @@ module.exports = {
|
||||||
const verifiedRole = interaction.options.getRole("verifiedrole");
|
const verifiedRole = interaction.options.getRole("verifiedrole");
|
||||||
const emailDomains = interaction.options
|
const emailDomains = interaction.options
|
||||||
.getString("emaildomains")
|
.getString("emaildomains")
|
||||||
.split(",")
|
.split(",");
|
||||||
.map((domain) => domain.trim());
|
|
||||||
const actionitemschannel =
|
|
||||||
interaction.options.getChannel("actionitemschannel");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Store the channel IDs instead of names
|
// Store the channel IDs instead of names
|
||||||
|
@ -74,14 +63,11 @@ module.exports = {
|
||||||
{ guildId: interaction.guild.id },
|
{ guildId: interaction.guild.id },
|
||||||
{
|
{
|
||||||
guildId: interaction.guild.id,
|
guildId: interaction.guild.id,
|
||||||
logChannelId: logChannel.id,
|
logChannelId: logChannel.id, // Store log channel ID
|
||||||
verifiedRoleName: verifiedRole.name,
|
verifiedRoleName: verifiedRole.name,
|
||||||
verificationChannelId: verificationChannel.id,
|
verificationChannelId: verificationChannel.id, // Store verification channel ID
|
||||||
generalChannelId: generalChannel.id,
|
generalChannelId: generalChannel.id, // Store general channel ID
|
||||||
emailDomains: emailDomains,
|
emailDomains: emailDomains,
|
||||||
actionItemsChannelId: actionitemschannel
|
|
||||||
? actionitemschannel.id
|
|
||||||
: null,
|
|
||||||
},
|
},
|
||||||
{ upsert: true, new: true }
|
{ upsert: true, new: true }
|
||||||
);
|
);
|
||||||
|
@ -92,10 +78,7 @@ module.exports = {
|
||||||
**General Channel**: <#${generalChannel.id}>\n
|
**General Channel**: <#${generalChannel.id}>\n
|
||||||
**Verification Channel**: <#${verificationChannel.id}>\n
|
**Verification Channel**: <#${verificationChannel.id}>\n
|
||||||
**Verified Role**: ${verifiedRole.name}\n
|
**Verified Role**: ${verifiedRole.name}\n
|
||||||
**Allowed Email Domains**: ${emailDomains.join(", ")}\n
|
**Allowed Email Domains**: ${emailDomains.join(", ")}`,
|
||||||
**Action Item Channel**: ${
|
|
||||||
actionitemschannel ? `<#${actionitemschannel.id}>` : "None"
|
|
||||||
}`,
|
|
||||||
ephemeral: true,
|
ephemeral: true,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("stats")
|
|
||||||
.setDescription("Displays server statistics."),
|
|
||||||
|
|
||||||
async execute(interaction) {
|
|
||||||
try {
|
|
||||||
const totalMembers = interaction.guild.memberCount;
|
|
||||||
|
|
||||||
const onlineMembers = interaction.guild.members.cache.filter(
|
|
||||||
(member) => member.presence?.status !== "offline"
|
|
||||||
).size;
|
|
||||||
|
|
||||||
const offlineMembers = totalMembers - onlineMembers;
|
|
||||||
const humanMembers = interaction.guild.members.cache.filter(
|
|
||||||
(member) => !member.user.bot
|
|
||||||
).size;
|
|
||||||
const botMembers = totalMembers - humanMembers;
|
|
||||||
|
|
||||||
const statsEmbed = new EmbedBuilder()
|
|
||||||
.setColor("#0099ff")
|
|
||||||
.setTitle("Server Statistics")
|
|
||||||
.addFields(
|
|
||||||
{
|
|
||||||
name: "Total Members",
|
|
||||||
value: totalMembers.toString(),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Online Members",
|
|
||||||
value: onlineMembers.toString(),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Offline Members",
|
|
||||||
value: offlineMembers.toString(),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Human Members",
|
|
||||||
value: humanMembers.toString(),
|
|
||||||
inline: true,
|
|
||||||
},
|
|
||||||
{ name: "Bot Members", value: botMembers.toString(), inline: true }
|
|
||||||
)
|
|
||||||
.setTimestamp()
|
|
||||||
.setFooter({
|
|
||||||
text: `Requested by ${interaction.user.tag}`,
|
|
||||||
iconURL: interaction.user.displayAvatarURL(),
|
|
||||||
});
|
|
||||||
|
|
||||||
await interaction.reply({ embeds: [statsEmbed] });
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error executing the stats command:", error);
|
|
||||||
await interaction.reply({
|
|
||||||
content: "There was an error while executing this command!",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
22
index.js
22
index.js
|
@ -5,14 +5,12 @@ const {
|
||||||
Collection,
|
Collection,
|
||||||
REST,
|
REST,
|
||||||
Routes,
|
Routes,
|
||||||
PresenceUpdateStatus,
|
|
||||||
} = require("discord.js");
|
} = require("discord.js");
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const ServerSettings = require("./models/ServerSettings");
|
const ServerSettings = require("./models/ServerSettings");
|
||||||
const seedShopItems = require("./utils/seedShopItems");
|
const seedShopItems = require("./utils/seedShopItems");
|
||||||
const seedSpyfallLocations = require("./utils/seedSpyfallLocations");
|
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
|
@ -67,18 +65,19 @@ client.once("ready", async () => {
|
||||||
// Register commands for all existing guilds
|
// Register commands for all existing guilds
|
||||||
const guilds = client.guilds.cache.map((guild) => guild.id);
|
const guilds = client.guilds.cache.map((guild) => guild.id);
|
||||||
|
|
||||||
await Promise.all(
|
// Seed the shop items
|
||||||
guilds.map(async (guildId) => {
|
for (const guildId of guilds) {
|
||||||
await seedShopItems(guildId);
|
await seedShopItems(guildId); // Pass guildId to seedShopItems
|
||||||
await seedSpyfallLocations(guildId);
|
}
|
||||||
await registerCommands(guildId);
|
|
||||||
})
|
for (const guildId of guilds) {
|
||||||
);
|
await registerCommands(guildId);
|
||||||
|
}
|
||||||
|
|
||||||
// Set bot status and activity
|
// Set bot status and activity
|
||||||
client.user.setPresence({
|
client.user.setPresence({
|
||||||
activities: [{ name: "Degenerate Gamers!", type: 3 }],
|
activities: [{ name: "Degenerate Gamers!", type: 3 }],
|
||||||
status: PresenceUpdateStatus.Online,
|
status: "online",
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`\n==============================\n`);
|
console.log(`\n==============================\n`);
|
||||||
|
@ -94,9 +93,6 @@ client.on("guildCreate", async (guild) => {
|
||||||
// seed items for new guild with guildId
|
// seed items for new guild with guildId
|
||||||
await seedShopItems(guild.id);
|
await seedShopItems(guild.id);
|
||||||
|
|
||||||
// Seed spyfall locations for the new guild
|
|
||||||
await seedSpyfallLocations(guild.id);
|
|
||||||
|
|
||||||
// Register slash commands for the new guild
|
// Register slash commands for the new guild
|
||||||
await registerCommands(guild.id);
|
await registerCommands(guild.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
const Participant = require("./Participant");
|
|
||||||
|
|
||||||
const eventSchema = new mongoose.Schema({
|
|
||||||
name: { type: String, required: true, unique: true },
|
|
||||||
description: { type: String, required: true },
|
|
||||||
category: {
|
|
||||||
type: String,
|
|
||||||
enum: ["tournamets", "meeting", "giveaway", "other"],
|
|
||||||
default: "other",
|
|
||||||
},
|
|
||||||
location: { type: String, default: "Online" },
|
|
||||||
startDate: { type: Date, required: true },
|
|
||||||
endDate: { type: Date, required: true },
|
|
||||||
organizerId: { type: String, required: true },
|
|
||||||
participants: [{ type: mongoose.Schema.Types.ObjectId, ref: "Participant" }],
|
|
||||||
recurrence: {
|
|
||||||
type: String,
|
|
||||||
enum: ["none", "daily", "weekly", "monthly"],
|
|
||||||
default: "none",
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
type: String,
|
|
||||||
enum: ["upcoming", "completed", "cancelled"],
|
|
||||||
default: "upcoming",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("Event", eventSchema);
|
|
|
@ -1,8 +0,0 @@
|
||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
const participantSchema = new mongoose.Schema({
|
|
||||||
userId: { type: String, required: true },
|
|
||||||
username: { type: String, required: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("Participant", participantSchema);
|
|
|
@ -7,9 +7,7 @@ const ServerSettingsSchema = new mongoose.Schema({
|
||||||
verificationChannelId: { type: String, required: false },
|
verificationChannelId: { type: String, required: false },
|
||||||
generalChannelId: { type: String, required: false },
|
generalChannelId: { type: String, required: false },
|
||||||
emailDomains: { type: [String], required: false },
|
emailDomains: { type: [String], required: false },
|
||||||
actionItemsChannelId: { type: String, required: false },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const ServerSettings = mongoose.model("ServerSettings", ServerSettingsSchema);
|
const ServerSettings = mongoose.model("ServerSettings", ServerSettingsSchema);
|
||||||
|
|
||||||
module.exports = ServerSettings;
|
module.exports = ServerSettings;
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
const mongoose = require("mongoose");
|
|
||||||
const { v4: uuidv4 } = require("uuid");
|
|
||||||
|
|
||||||
const gameStatusEnum = {
|
|
||||||
values: ["ongoing", "ended"],
|
|
||||||
message: 'Status must be either "ongoing" or "ended"',
|
|
||||||
};
|
|
||||||
|
|
||||||
const spyfallGameSchema = new mongoose.Schema({
|
|
||||||
gameId: { type: String, required: true, unique: true, default: uuidv4 },
|
|
||||||
guildId: { type: String, required: true },
|
|
||||||
location: { type: String, required: true },
|
|
||||||
spy: { type: String, required: true },
|
|
||||||
players: { type: [String], required: true },
|
|
||||||
status: { type: String, enum: gameStatusEnum, default: "ongoing" },
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("SpyfallGame", spyfallGameSchema);
|
|
|
@ -1,7 +0,0 @@
|
||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
const spyfallLocationSchema = new mongoose.Schema({
|
|
||||||
name: { type: String, required: true, unique: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = mongoose.model("SpyfallLocation", spyfallLocationSchema);
|
|
|
@ -16,11 +16,9 @@
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"html-entities": "^2.5.2",
|
"html-entities": "^2.5.2",
|
||||||
"moment": "^2.30.1",
|
|
||||||
"mongoose": "^8.6.0",
|
"mongoose": "^8.6.0",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.14",
|
||||||
"owoify-js": "^2.0.0",
|
"owoify-js": "^2.0.0",
|
||||||
"puppeteer": "^23.4.1",
|
"puppeteer": "^23.4.1"
|
||||||
"uuid": "^11.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
const SpyfallLocation = require("../models/SpyfallLocation");
|
|
||||||
|
|
||||||
async function seedSpyfallLocations(guildId) {
|
|
||||||
const locations = [
|
|
||||||
{ name: "Beach" },
|
|
||||||
{ name: "Casino" },
|
|
||||||
{ name: "Circus" },
|
|
||||||
{ name: "Cruise Ship" },
|
|
||||||
{ name: "Hospital" },
|
|
||||||
{ name: "Hotel" },
|
|
||||||
{ name: "Military Base" },
|
|
||||||
{ name: "Movie Studio" },
|
|
||||||
{ name: "Pirate Ship" },
|
|
||||||
{ name: "Polar Station" },
|
|
||||||
{ name: "Police Station" },
|
|
||||||
{ name: "Restaurant" },
|
|
||||||
{ name: "School" },
|
|
||||||
{ name: "Space Station" },
|
|
||||||
{ name: "Submarine" },
|
|
||||||
{ name: "Supermarket" },
|
|
||||||
{ name: "Theater" },
|
|
||||||
{ name: "University" },
|
|
||||||
{ name: "Zoo" },
|
|
||||||
{ name: "Airplane" },
|
|
||||||
{ name: "Bank" },
|
|
||||||
{ name: "Cathedral" },
|
|
||||||
{ name: "Corporate Party" },
|
|
||||||
{ name: "Crusader Army" },
|
|
||||||
{ name: "Day Spa" },
|
|
||||||
{ name: "Embassy" },
|
|
||||||
{ name: "Jail" },
|
|
||||||
{ name: "Museum" },
|
|
||||||
{ name: "Passenger Train" },
|
|
||||||
{ name: "Service Station" },
|
|
||||||
{ name: "Space Station" },
|
|
||||||
{ name: "Subway" },
|
|
||||||
{ name: "The U.N." },
|
|
||||||
{ name: "World Cup Final" },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const location of locations) {
|
|
||||||
await SpyfallLocation.updateOne(
|
|
||||||
{ name: location.name },
|
|
||||||
{ $set: location },
|
|
||||||
{ upsert: true }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`✅ Spyfall Locations seeded for guild: ${guildId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = seedSpyfallLocations;
|
|
Loading…
Reference in a new issue