diff --git a/src/i18n/en-GB.yml b/src/i18n/en-GB.yml index cb608e4..0e376ba 100644 --- a/src/i18n/en-GB.yml +++ b/src/i18n/en-GB.yml @@ -178,6 +178,21 @@ misc: blocked: description: You are not allowed to create tickets. title: ❌ Blocked + category_full: + description: | + The category has reached its maximum capacity. + Please try again later. + title: ❌ Category full + cooldown: + description: Please wait {time} before creating another ticket in this category. + title: ❌ Please wait + member_limit: + description: + - Please use your existing ticket or close it before creating another. + - Please close a ticket before creating another. + title: + - ❌ You already have a ticket + - ❌ You already have %d open tickets missing_roles: description: You do not have the roles required to be able to create a ticket in this category. title: ❌ Insufficient roles diff --git a/src/lib/tickets/manager.js b/src/lib/tickets/manager.js index 89c3bb1..b9d2ae7 100644 --- a/src/lib/tickets/manager.js +++ b/src/lib/tickets/manager.js @@ -89,56 +89,95 @@ module.exports = class TicketManager { ephemeral: true, }); } else { - this.client.keyv.set(rlKey, true, ms('10s')); + this.client.keyv.set(rlKey, true, ms('5s')); } + const sendError = name => interaction.reply({ + embeds: [ + new ExtendedEmbedBuilder({ + iconURL: interaction.guild.iconURL(), + text: category.guild.footer, + }) + .setColor(category.guild.errorColour) + .setTitle(getMessage(`misc.${name}.title`)) + .setDescription(getMessage(`misc.${name}.description`)), + ], + ephemeral: true, + }); + /** @type {import("discord.js").Guild} */ const guild = this.client.guilds.cache.get(category.guild.id); const member = interaction.member ?? await guild.members.fetch(interaction.user.id); if (category.guild.blocklist.length !== 0) { const blocked = category.guild.blocklist.some(r => member.roles.cache.has(r)); - if (blocked) { - return await interaction.reply({ - embeds: [ - new ExtendedEmbedBuilder({ - iconURL: interaction.guild.iconURL(), - text: category.guild.footer, - }) - .setColor(category.guild.errorColour) - .setTitle(getMessage('misc.blocked.title')) - .setDescription(getMessage('misc.blocked.description')), - ], - ephemeral: true, - }); - } + if (blocked) return await sendError('blocked'); } if (category.requiredRoles.length !== 0) { const missing = category.requiredRoles.some(r => !member.roles.cache.has(r)); - if (missing) { - return await interaction.reply({ - embeds: [ - new ExtendedEmbedBuilder({ - iconURL: interaction.guild.iconURL(), - text: category.guild.footer, - }) - .setColor(category.guild.errorColour) - .setTitle(getMessage('misc.missing_roles.title')) - .setDescription(getMessage('misc.missing_roles.description')), - ], - ephemeral: true, - }); - } + if (missing) return await sendError('missing_roles'); } - // TODO: if discordCategory has 50 channels -> stop + const discordCategory = guild.channels.cache.get(category.discordCategory); + if (discordCategory.children.cache.size === 50) return await sendError('category_full'); - // TODO: if category has max channels -> stop + // TODO: store locally and sync regularly so this isn't done during an interaction? + const totalCount = await this.client.prisma.ticket.count({ + where: { + categoryId: category.id, + open: true, + }, + }); + if (totalCount >= category.totalLimit) return await sendError('category_full'); - // TODO: if member has max -> stop + const memberCount = await this.client.prisma.ticket.count({ + where: { + categoryId: category.id, + createdById: interaction.user.id, + open: true, + }, + }); - // TODO: if cooldown -> stop + if (memberCount >= category.memberLimit) { + return await interaction.reply({ + embeds: [ + new ExtendedEmbedBuilder({ + iconURL: interaction.guild.iconURL(), + text: category.guild.footer, + }) + .setColor(category.guild.errorColour) + .setTitle(getMessage('misc.member_limit.title', memberCount, memberCount)) + .setDescription(getMessage('misc.member_limit.description', memberCount)), + ], + ephemeral: true, + }); + } + + const lastTicket = await this.client.prisma.ticket.findFirst({ + orderBy: [{ closedAt: 'desc' }], + select: { closedAt: true }, + where: { + categoryId: category.id, + createdById: interaction.user.id, + open: false, + }, + }); + + if (Date.now() - lastTicket.closedAt < category.cooldown) { + return await interaction.reply({ + embeds: [ + new ExtendedEmbedBuilder({ + iconURL: interaction.guild.iconURL(), + text: category.guild.footer, + }) + .setColor(category.guild.errorColour) + .setTitle(getMessage('misc.cooldown.title')) + .setDescription(getMessage('misc.cooldown.description', { time: ms(category.cooldown - (Date.now() - lastTicket.closedAt)) })), + ], + ephemeral: true, + }); + } if (category.questions.length >= 1) { await interaction.showModal(