Skip to content

Commit 5816d4b

Browse files
committed
[Feat]: implement ticket system enhancements with ephemeral responses and message management
1 parent b2916b6 commit 5816d4b

File tree

5 files changed

+46
-30
lines changed

5 files changed

+46
-30
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ coverage/
77

88

99
.idea/
10+
11+
config/

config/config.json

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/commands/ticket-message.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { SlashCommandBuilder, PermissionsBitField } from 'discord.js';
22
import { buildEmbed } from '../utils/embed';
3-
import { getGuildConfig } from '../config/store';
3+
import { getGuildConfig, upsertGuildConfig } from '../config/store';
44

55
const data = new SlashCommandBuilder()
66
.setName('ticketmessage')
@@ -11,22 +11,37 @@ const data = new SlashCommandBuilder()
1111

1212
async function execute(interaction: any) {
1313
if (!interaction.memberPermissions?.has(PermissionsBitField.Flags.ManageChannels)) {
14-
return interaction.reply({ content: 'Solo junta/admin.', ephemeral: true });
14+
return interaction.reply({ content: 'Solo junta/admin.', flags: 1 << 6 });
1515
}
1616
const cfg = getGuildConfig(interaction.guildId);
1717
if (!cfg?.channels.ticketTrigger) {
18-
return interaction.reply({ content: 'Configura primero /setup', ephemeral: true });
18+
return interaction.reply({ content: 'Configura primero /setup', flags: 1 << 6 });
1919
}
2020
const channel = interaction.guild.channels.cache.get(cfg.channels.ticketTrigger);
2121
if (!channel || !channel.isTextBased?.()) {
22-
return interaction.reply({ content: 'Canal de tickets inválido', ephemeral: true });
22+
return interaction.reply({ content: 'Canal de tickets inválido', flags: 1 << 6 });
2323
}
2424
const description =
2525
interaction.options.getString('description') ?? 'Reacciona con 🎫 para crear un ticket.';
2626
const embed = buildEmbed({ title: 'Tickets', description });
27+
28+
// delete previous ticket message if stored
29+
if (cfg.ticketMessageId) {
30+
try {
31+
const prev = await channel.messages.fetch(cfg.ticketMessageId).catch(() => null);
32+
if (prev?.deletable) await prev.delete();
33+
} catch (err) {
34+
// ignore fetch/delete errors
35+
}
36+
}
37+
2738
const msg = await channel.send({ embeds: [embed] });
2839
await msg.react('🎫');
29-
await interaction.reply({ content: 'Mensaje de tickets publicado.', ephemeral: true });
40+
41+
// persist new message id
42+
upsertGuildConfig({ ...cfg, guildId: interaction.guildId, ticketMessageId: msg.id });
43+
44+
await interaction.reply({ content: 'Mensaje de tickets publicado.', flags: 1 << 6 });
3045
}
3146

3247
export default { data, execute };

src/config/store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface GuildConfig {
1414
vcPool?: string[];
1515
voiceCategory?: string;
1616
};
17+
ticketMessageId?: string;
1718
}
1819

1920
interface ConfigFile {

src/events/messageReactionAdd.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { buildEmbed } from '../utils/embed';
22
import { getGuildConfig } from '../config/store';
33

4+
const EPHEMERAL_FLAG = 1 << 6; // Discord API flag
5+
46
// Ticket creation via reaction on the configured ticket trigger channel/message
57
export default {
68
name: 'messageReactionAdd',
@@ -19,10 +21,27 @@ export default {
1921
if (reaction.emoji?.name !== '🎫') return;
2022

2123
// Remove the user's reaction to keep the trigger clean
22-
try {
23-
await reaction.users.remove(user.id).catch(() => {});
24-
} catch (err) {
25-
console.error('Failed to remove reaction', err);
24+
await reaction.users.remove(user.id).catch(() => {});
25+
26+
// Enforce one open ticket per user: if a category with their ticket name exists, ping them in their ticket text channel instead of creating a new one.
27+
const existingCategory: any = guild.channels.cache.find(
28+
(ch: any) => ch.type === 4 && ch.name === `ticket-${user.username}`,
29+
);
30+
if (existingCategory) {
31+
const ticketText = guild.channels.cache.find(
32+
(ch: any) =>
33+
ch.parentId === existingCategory.id && ch.name === `ticket-${user.username}-txt`,
34+
);
35+
const notifyPayload = {
36+
content: `<@${user.id}> ya tienes un ticket abierto.`,
37+
flags: EPHEMERAL_FLAG,
38+
};
39+
if (ticketText && ticketText.isTextBased?.()) {
40+
await ticketText.send(notifyPayload).catch(() => {});
41+
} else {
42+
await reaction.message.channel.send(notifyPayload).catch(() => {});
43+
}
44+
return;
2645
}
2746

2847
// Create category for the ticket

0 commit comments

Comments
 (0)