(modals): Implement open-mod-ticket modal.

This commit is contained in:
2026-10-14 16:00:46 +08:00
parent 96c783fb96
commit 741b56a3dd
3 changed files with 32 additions and 35 deletions
+23 -15
View File
@@ -1,31 +1,39 @@
import { Client, MessageFlags, ModalSubmitInteraction } from 'discord.js'; import { Client, MessageFlags, ModalSubmitInteraction } from 'discord.js';
import { Database } from '../shared/Database'; import { Database } from '../shared/Database';
import { createPrivateHelpTicketThread } from '../utils'; import { createPrivateHelpTicketThread } from '../utils';
import { Modal } from '../types';
const warning = '\n\n\nLeaving this thread without acknowledgement may result in punishment. Staff will close the thread when they deem your response acceptable.'; const warning = '\n\n\nLeaving this thread without acknowledgement may result in punishment. Staff will close the thread when they deem your response acceptable.';
export default { class OpenModTicketModal implements Modal<Client> {
name: 'open-mod-ticket', id = 'open-mod-ticket';
handler: async function (client: Client, interaction: ModalSubmitInteraction, userId: string) {
const guild = await client.guilds.fetch(interaction.guildId!); async execute(context: Client<boolean>, interaction: ModalSubmitInteraction, userId: string): Promise<void> {
if (!interaction.inGuild()) return;
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
try {
const settings = await Database.getOrCreateSettings(interaction.guildId);
if (settings.private_help_channel_id) throw new Error("`private_help_channel_id` hasn't been configured. Please report this error to staff.");
const guild = await context.guilds.fetch(interaction.guildId);
const member = await guild.members.fetch(userId); const member = await guild.members.fetch(userId);
const guildSettings = await Database.getOrCreateSettings(guild.id);
if (!guildSettings || !guildSettings.private_help_channel_id)
return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket. Please report this to a developer.' });
const title = interaction.fields.getTextInputValue('title') ? interaction.fields.getTextInputValue('title') : `Mod Ticket For ${member.displayName}`; const title = interaction.fields.getTextInputValue('title') ? interaction.fields.getTextInputValue('title') : `Mod Ticket For ${member.displayName}`;
const reason = interaction.fields.getTextInputValue('initial-message') + warning; const reason = interaction.fields.getTextInputValue('initial-message') + warning;
const autoJoin = interaction.fields.getStringSelectValues('auto-join-thread')[0] == 'yes'; const autoJoin = interaction.fields.getCheckbox('auto-join-thread');
const membersToAdd = [userId]; const membersToAdd = [userId];
if (autoJoin) membersToAdd.push(interaction.user.id); if (autoJoin) membersToAdd.push(interaction.user.id);
const thread = await createPrivateHelpTicketThread(client, guild, null, reason, title, membersToAdd); const thread = await createPrivateHelpTicketThread(context, guild, null, reason, title, membersToAdd);
if (!thread) throw new Error('Failed to create a ticket thread. Please report this to a developer.');
if (thread) interaction.reply({ flags: [MessageFlags.Ephemeral], content: `Mod ticket created: ${thread}.${!autoJoin ? "You've selected not to auto join the thread. You will not be notified of messages sent there. You will have to check the thread periodically for user response." : ''}` }); await interaction.editReply({ content: `Mod ticket created: ${thread}. ${!autoJoin ? "You've selected not to auto join the thread. You will not be notified of messages sent there. You will have to check the thread periodically for user response." : ''}` });
else interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Failed to create ticket. Please report this to a developer.' }); } catch (error: Error | any) {
await interaction.editReply({ content: error.message }).catch(() => {});
} }
}; }
}
export default new OpenModTicketModal();
+5 -15
View File
@@ -1,4 +1,4 @@
import { LabelBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextInputBuilder, TextInputStyle } from 'discord.js'; import { CheckboxBuilder, LabelBuilder, TextInputBuilder, TextInputStyle } from 'discord.js';
export function createTextInput(customId: string, labelTitle: string, description: string | null, required: boolean, style: TextInputStyle, maxLength: number | null, minLength: number | null): LabelBuilder { export function createTextInput(customId: string, labelTitle: string, description: string | null, required: boolean, style: TextInputStyle, maxLength: number | null, minLength: number | null): LabelBuilder {
const input = new TextInputBuilder() const input = new TextInputBuilder()
@@ -19,23 +19,13 @@ export function createTextInput(customId: string, labelTitle: string, descriptio
return label; return label;
} }
export function createYesNoMenu(customId: string, labelTitle: string, description: string | null, defaultYes: boolean): LabelBuilder { export function createCheckbox(customId: string, labelTitle: string, description: string | null, defaultYes: boolean): LabelBuilder {
const yesNoMenu = new StringSelectMenuBuilder() const component = new CheckboxBuilder()
.setCustomId(customId) .setCustomId(customId);
.addOptions(
new StringSelectMenuOptionBuilder()
.setLabel('Yes')
.setDefault(defaultYes)
.setValue('yes'),
new StringSelectMenuOptionBuilder()
.setLabel('No')
.setDefault(!defaultYes)
.setValue('no')
);
const yesNoLabel = new LabelBuilder() const yesNoLabel = new LabelBuilder()
.setLabel(labelTitle) .setLabel(labelTitle)
.setStringSelectMenuComponent(yesNoMenu); .setCheckboxComponent(component);
if (description !== null) yesNoLabel.setDescription(description); if (description !== null) yesNoLabel.setDescription(description);
+2 -3
View File
@@ -1,6 +1,6 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, ModalBuilder, PrivateThreadChannel, TextChannel, TextInputStyle, ThreadAutoArchiveDuration, ThreadChannel, UserContextMenuCommandInteraction } from 'discord.js'; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, ModalBuilder, PrivateThreadChannel, TextChannel, TextInputStyle, ThreadAutoArchiveDuration, ThreadChannel, UserContextMenuCommandInteraction } from 'discord.js';
import { Database } from '../shared/Database'; import { Database } from '../shared/Database';
import { createTextInput, createYesNoMenu } from './modal-utils'; import { createTextInput, createCheckbox } from './modal-utils';
export async function closeOldTickets(client: Client) { export async function closeOldTickets(client: Client) {
for (const ticket of await Database.getAllOpenPrivateHelpTickets()) { for (const ticket of await Database.getAllOpenPrivateHelpTickets()) {
@@ -92,9 +92,8 @@ export async function openModTicketModal(interaction: UserContextMenuCommandInte
const titleLabel = createTextInput('title', 'Title', `The name of the thread. Defaults to "Mod Ticket For ${member.displayName}" if left empty`, false, TextInputStyle.Short, 100, null); const titleLabel = createTextInput('title', 'Title', `The name of the thread. Defaults to "Mod Ticket For ${member.displayName}" if left empty`, false, TextInputStyle.Short, 100, null);
const initialMessageLabel = createTextInput('initial-message', 'Inital Message', 'The inital message sent in the thread', false, TextInputStyle.Paragraph, 1800, null); const initialMessageLabel = createTextInput('initial-message', 'Inital Message', 'The inital message sent in the thread', false, TextInputStyle.Paragraph, 1800, null);
const autoJoinLabel = createYesNoMenu('auto-join-thread', 'Auto Join Thread', 'Whether or not to join you to the thread. Selecting no will not notify you of messages sent!', true); const autoJoinLabel = createCheckbox('auto-join-thread', 'Auto Join Thread', 'Whether or not to automatically add you to the thread.', true);
modal.addLabelComponents(titleLabel, initialMessageLabel, autoJoinLabel); modal.addLabelComponents(titleLabel, initialMessageLabel, autoJoinLabel);
interaction.showModal(modal); interaction.showModal(modal);
} }