Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/commands/admin/event/createevent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
import { ConfigService } from '#services/configService.js';
import logger from '#utils/logger.js';
import { EventService } from '#services/eventService.js';

export const data = new SlashCommandBuilder()
.setName('create-event')
.setDescription('Create an event')
.addStringOption((option) =>
option.setName('name').setDescription('The name of the event').setRequired(true)
)
.addStringOption((option) =>
option.setName('description').setDescription('The description of the event').setRequired(false)
)
.addStringOption((option) =>
option.setName('startdate').setDescription('The start date of the event').setRequired(false)
)
.addStringOption((option) =>
option.setName('enddate').setDescription('The end date of the event').setRequired(false)
);

/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
if (!interaction.member.permissions.has('Administrator')) {
const customRole = await ConfigService.getAdminRole(interaction.guildId);
if (customRole) {
const member = await interaction.guild.members.fetch(interaction.user.id);
if (!member.roles.cache.has(customRole)) {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
} else {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
}

const name = interaction.options.getString('name');
const description = interaction.options.getString('description');
const startDate = interaction.options.getString('startdate');
const endDate = interaction.options.getString('enddate');

// validate datetime format
const dateFormat = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/;
if (startDate && !dateFormat.test(startDate)) {
await interaction.reply('Invalid start date format. Please use ISO 8601 format.');
return;
}
if (endDate && !dateFormat.test(endDate)) {
await interaction.reply('Invalid end date format. Please use ISO 8601 format.');
return;
}

try {
await EventService.createEvent(name, description, startDate, endDate);
await interaction.reply(`Event "${name}" created successfully!`);
logger.info(`Event "${name}" created successfully by ${interaction.user.tag}`);
} catch (error) {
await interaction.reply('An error occurred while creating the event. Please try again.');
}
}
108 changes: 108 additions & 0 deletions src/commands/admin/event/editevent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
import { ConfigService } from '#services/configService.js';
import logger from '#utils/logger.js';
import { EventService } from '#services/eventService.js';

export const data = new SlashCommandBuilder()
.setName('edit-event')
.setDescription('Edit an event')
.addStringOption((option) =>
option
.setName('event')
.setDescription('Select the event to edit')
.setRequired(true)
.setAutocomplete(true)
)
.addStringOption((option) =>
option.setName('name').setDescription('The name of the event').setRequired(false)
)
.addStringOption((option) =>
option.setName('description').setDescription('The description of the event').setRequired(false)
)
.addStringOption((option) =>
option.setName('startdate').setDescription('The start date of the event').setRequired(false)
)
.addStringOption((option) =>
option.setName('enddate').setDescription('The end date of the event').setRequired(false)
)
.addStringOption((option) =>
option
.setName('status')
.setDescription('The status of the event')
.setRequired(false)
.addChoices(
{ name: 'Planning', value: 'PLANNING' },
{ name: 'Ongoing', value: 'ONGOING' },
{ name: 'Completed', value: 'COMPLETED' },
{ name: 'Canceled', value: 'CANCELLED' }
)
);

/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
if (!interaction.member.permissions.has('Administrator')) {
const customRole = await ConfigService.getAdminRole(interaction.guildId);
if (customRole) {
const member = await interaction.guild.members.fetch(interaction.user.id);
if (!member.roles.cache.has(customRole)) {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
} else {
await interaction.reply('You do not have permission to set the approval channel.');
return;
}
}

const eventId = interaction.options.getString('event');
const name = interaction.options.getString('name');
const description = interaction.options.getString('description');
const startDate = interaction.options.getString('startdate');
const endDate = interaction.options.getString('enddate');
const status = interaction.options.getString('status');

if (!name && !description && !startDate && !endDate && !status) {
await interaction.reply('Please provide at least one field to edit.');
return;
}
// validate datetime format
const dateFormat = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/;
if (startDate && !dateFormat.test(startDate)) {
await interaction.reply('Invalid start date format. Please use ISO 8601 format.');
return;
}
if (endDate && !dateFormat.test(endDate)) {
await interaction.reply('Invalid end date format. Please use ISO 8601 format.');
return;
}

try {
const updatedEvent = await EventService.updateEvent(
eventId,
name,
description,
startDate,
endDate,
status
);
await interaction.reply(`Event "${updatedEvent.name}" edited successfully!`);
logger.info(
`Event "${updatedEvent.name}" (${updatedEvent.id}) edited successfully by ${interaction.user.tag}`
);
} catch (error) {
await interaction.reply('An error occurred while editing the event. Please try again.');
}
}

/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function autocomplete(interaction) {
const focusedValue = interaction.options.getFocused();
const events = await EventService.getEvents();
const filtered = events.filter((event) => event.name.startsWith(focusedValue));
const choices = filtered.map((event) => event).slice(0, 25);
await interaction.respond(choices.map((choice) => ({ name: choice.name, value: choice.id })));
}
46 changes: 34 additions & 12 deletions src/events/interactionCreate.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
import { Events, MessageFlags } from 'discord.js';
import { Events, MessageFlags, ChatInputCommandInteraction } from 'discord.js';
import logger from '#utils/logger.js';

export const name = Events.InteractionCreate;
/**
* @param {ChatInputCommandInteraction} interaction
*/
export async function execute(interaction) {
if (!interaction.isChatInputCommand()) return;
if (interaction.isChatInputCommand()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;

const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;
try {
if (interaction.isAutocomplete()) {
await command.autocomplete(interaction);
} else {
await command.execute(interaction);
}
} catch (error) {
logger.error(error);
await interaction.reply({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral,
});
}
}

if (interaction.isAutocomplete()) {
const command = interaction.client.commands.get(interaction.commandName);
if (!command) return;

try {
await command.execute(interaction);
} catch (error) {
logger.error(error);
await interaction.reply({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral,
});
try {
await command.autocomplete(interaction);
} catch (error) {
logger.error(error);
await interaction.reply({
content: 'There was an error while executing this command!',
flags: MessageFlags.Ephemeral,
});
}
}
}
98 changes: 98 additions & 0 deletions src/services/eventService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import prisma from '#utils/prisma.js';
import logger from '#utils/logger.js';

export class EventService {
/**
* Create a new event in the database
* @param {String} name - The name of the event
* @param {String} description - The description of the event
* @param {String} startDate - (Optional) The start date of the event
* @param {String} endDate - (Optional) The end date of the event
* @returns {Promise<void>}
*/
static async createEvent(name, description, startDate = undefined, endDate = undefined) {
// Validate inputs
if (!name) {
throw new Error('Event name is required');
}
// Create the event
try {
const eventData = { name, description };
if (startDate) eventData.startDate = startDate;
eventData.startDate = new Date().toISOString();
if (endDate !== undefined) eventData.endDate = endDate;

await prisma.event.create({
data: eventData,
});
} catch (error) {
console.log(error);
logger.error('Error creating event', error.message);
throw error;
}
}

/**
* Get all events
* @returns {Promise<import('@prisma/client').Event[]>} An array of events
*/
static async getEvents() {
try {
const events = await prisma.event.findMany({
orderBy: {
startDate: 'asc',
},
});
return events;
} catch (error) {
logger.error('Error getting events', error);
throw error;
}
}

/**
* Delete an event by ID
* @param {String} eventId - The ID of the event
* @returns {Promise<void>}
*/
static async deleteEvent(eventId) {
try {
await prisma.event.delete({
where: {
id: eventId,
},
});
} catch (error) {
logger.error(`Error deleting event with ID ${eventId}:`, error);
throw error;
}
}

/**
* Update an event by ID
* @param {String} eventId - The ID of the event
* @param {Object} data - The data to update
* @returns {Promise<import('@prisma/client').Event}
*/
static async updateEvent(eventId, name, description, startDate, endDate, status) {
const updateData = {};
try {
if (name) updateData.name = name;
if (description) updateData.description = description;
if (startDate) updateData.startDate = startDate;
if (endDate) updateData.endDate = endDate;
if (status) updateData.status = status;

return await prisma.event.update({
where: {
id: eventId,
},
data: updateData,
});
} catch (error) {
console.log(error);
logger.error(`Error updating event with ID ${eventId}:`, error, updateData);
throw error;
}
}
}