diff --git a/src/buttons/private-help.ts b/src/buttons/private-help.ts index fb94585..6c3d991 100644 --- a/src/buttons/private-help.ts +++ b/src/buttons/private-help.ts @@ -1,5 +1,5 @@ -import { ActionRowBuilder, ButtonInteraction, Client, ModalBuilder, TextInputBuilder, TextInputStyle, MessageFlags } from 'discord.js'; -import { canOpenPrivateHelpTicket } from '../utils'; +import { ButtonInteraction, Client, ModalBuilder, TextInputStyle, MessageFlags } from 'discord.js'; +import { canOpenPrivateHelpTicket, createTextInput } from '../utils'; export default { name: 'private-help', @@ -11,16 +11,9 @@ export default { .setCustomId('open-ticket-modal') .setTitle('Get in contact'); - const input = new TextInputBuilder() - .setCustomId('ticket-message') - .setLabel('What is the reason for your ticket?') - .setStyle(TextInputStyle.Paragraph) - .setPlaceholder('Please be as thorough as possible.') - .setRequired(true) - .setMinLength(10) - .setMaxLength(1500); + const label = createTextInput('ticket-message', 'What is the reason for your ticket?', null, true, TextInputStyle.Paragraph, 1500, 10); - modal.addComponents(new ActionRowBuilder().addComponents(input)); + modal.addLabelComponents(label); await interaction.showModal(modal); } diff --git a/src/commands/knowledgebase.ts b/src/commands/knowledgebase.ts index bc00a06..8394006 100644 --- a/src/commands/knowledgebase.ts +++ b/src/commands/knowledgebase.ts @@ -1,7 +1,7 @@ -import { ActionRowBuilder, ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, ModalBuilder, PermissionFlagsBits, SlashCommandBuilder, TextInputBuilder, TextInputStyle } from 'discord.js'; +import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, ModalBuilder, PermissionFlagsBits, SlashCommandBuilder, TextInputStyle } from 'discord.js'; import { Database } from '../shared/Database'; import { KnowledgebaseItem } from '../types'; -import { deferInteraction, logCustomEvent } from '../utils'; +import { createTextInput, deferInteraction, logCustomEvent } from '../utils'; export default { name: 'knowledgebase', @@ -50,24 +50,10 @@ export default { .setCustomId('add-knowledgebase-item-modal') .setTitle('Add to knowledgebase'); - const name = new TextInputBuilder() - .setCustomId('name') - .setLabel('Knowledgebase item name') - .setStyle(TextInputStyle.Short) - .setRequired(true) - .setMinLength(1) - .setMaxLength(300); + const nameLabel = createTextInput('name', 'Knowledgebase Item Name', null, true, TextInputStyle.Short, 300, 1); + const contentLabel = createTextInput('content', 'Item Content', null, true, TextInputStyle.Paragraph, 2000, 1); - const content = new TextInputBuilder() - .setCustomId('content') - .setLabel('Item content') - .setStyle(TextInputStyle.Paragraph) - .setRequired(true) - .setMinLength(1) - .setMaxLength(2000); - - modal.addComponents(new ActionRowBuilder().addComponents(name)); - modal.addComponents(new ActionRowBuilder().addComponents(content)); + modal.addLabelComponents(nameLabel, contentLabel); return await interaction.showModal(modal); } else if (subcommand == 'remove') { @@ -116,15 +102,9 @@ export default { .setCustomId(`edit-knowledgebase-item-modal_${id}`) .setTitle(`Editing knowledgebase item ${existingItem.name.slice(0, 18)}`); - const content = new TextInputBuilder() - .setCustomId('content') - .setLabel('New content') - .setStyle(TextInputStyle.Paragraph) - .setRequired(true) - .setMinLength(1) - .setMaxLength(2000); + const contentLabel = createTextInput('content', 'New Content', null, true, TextInputStyle.Paragraph, 2000, 1); - modal.addComponents(new ActionRowBuilder().addComponents(content)); + modal.addLabelComponents(contentLabel); return await interaction.showModal(modal); } diff --git a/src/context-menus/add-note.ts b/src/context-menus/add-note.ts index 4b36b51..8d90ea8 100644 --- a/src/context-menus/add-note.ts +++ b/src/context-menus/add-note.ts @@ -1,4 +1,5 @@ -import { ActionRowBuilder, ApplicationCommandType, ApplicationIntegrationType, Client, ContextMenuCommandBuilder, InteractionContextType, ModalBuilder, PermissionFlagsBits, TextInputBuilder, TextInputStyle, UserContextMenuCommandInteraction } from 'discord.js'; +import { ApplicationCommandType, ApplicationIntegrationType, Client, ContextMenuCommandBuilder, InteractionContextType, ModalBuilder, PermissionFlagsBits, TextInputStyle, UserContextMenuCommandInteraction } from 'discord.js'; +import { createTextInput } from '../utils'; export default { name: 'Add Note', @@ -17,15 +18,9 @@ export default { .setCustomId(`add-note-modal_${idToUse}`) .setTitle(`Adding note to ${member ? member.displayName : idToUse}`); - const input = new TextInputBuilder() - .setCustomId('note-message') - .setLabel('Note message') - .setStyle(TextInputStyle.Paragraph) - .setRequired(true) - .setMinLength(2) - .setMaxLength(1500); + const inputLabel = createTextInput('note-message', 'Note Message', null, true, TextInputStyle.Paragraph, 1500, 2); - modal.addComponents(new ActionRowBuilder().addComponents(input)); + modal.addLabelComponents(inputLabel); await interaction.showModal(modal); } diff --git a/src/context-menus/sync-name.ts b/src/context-menus/sync-name.ts index ab2c1ee..3af5bf5 100644 --- a/src/context-menus/sync-name.ts +++ b/src/context-menus/sync-name.ts @@ -1,7 +1,7 @@ import { ActionRowBuilder, ApplicationCommandType, ApplicationIntegrationType, Client, ContextMenuCommandBuilder, InteractionContextType, ModalBuilder, PermissionFlagsBits, TextInputBuilder, TextInputStyle, UserContextMenuCommandInteraction } from 'discord.js'; import { config } from '../config'; import { Database } from '../shared/Database'; -import { deferInteraction, syncName } from '../utils'; +import { createTextInput, deferInteraction, syncName } from '../utils'; export default { name: 'Sync Name', @@ -39,13 +39,9 @@ export default { .setCustomId(`sync-name-modal_${idToUse}`) .setTitle(`Syncing ${member ? member.displayName : idToUse}'s name`); - const input = new TextInputBuilder() - .setCustomId('id') - .setLabel('User has multiple linked accounts. Provide ID') - .setStyle(TextInputStyle.Short) - .setRequired(false); + const inputLabel = createTextInput('id', 'User has multiple linked accounts. Provide ID', null, false, TextInputStyle.Short, null, null); - modal.addComponents(new ActionRowBuilder().addComponents(input)); + modal.addLabelComponents(inputLabel); return await interaction.showModal(modal); } diff --git a/src/utils/index.ts b/src/utils/index.ts index 96dd3b0..2ced0b1 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -12,7 +12,9 @@ export * from './event-log-utils'; export * from './file-utils'; export * from './github-user-utils'; export * from './interaction-utils'; +export * from './message-matcher-regex'; export * from './message-utils'; +export * from './modal-utils'; export * from './ms-to-human'; export * from './name-sync'; export * from './note-utils'; @@ -20,7 +22,6 @@ export * from './oauth2'; export * from './private-help-utils'; export * from './record-utils'; export * from './refresh-commands'; -export * from './message-matcher-regex'; export * from './string-utils'; export * from './ticket-events'; export * from './ticket-utils'; diff --git a/src/utils/modal-utils.ts b/src/utils/modal-utils.ts new file mode 100644 index 0000000..7a65cd8 --- /dev/null +++ b/src/utils/modal-utils.ts @@ -0,0 +1,43 @@ +import { LabelBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, 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 { + const input = new TextInputBuilder() + .setCustomId(customId) + .setStyle(style) + .setRequired(required); + + if (maxLength !== null) input.setMaxLength(maxLength); + if (minLength !== null) input.setMinLength(minLength); + + const label = new LabelBuilder() + .setLabel(labelTitle); + + if (description !== null) label.setDescription(description); + + label.setTextInputComponent(input); + + return label; +} + +export function createYesNoMenu(customId: string, labelTitle: string, description: string | null, defaultYes: boolean): LabelBuilder { + const yesNoMenu = new StringSelectMenuBuilder() + .setCustomId(customId) + .addOptions( + new StringSelectMenuOptionBuilder() + .setLabel('Yes') + .setDefault(defaultYes) + .setValue('yes'), + new StringSelectMenuOptionBuilder() + .setLabel('No') + .setDefault(!defaultYes) + .setValue('no') + ); + + const yesNoLabel = new LabelBuilder() + .setLabel(labelTitle) + .setStringSelectMenuComponent(yesNoMenu); + + if (description !== null) yesNoLabel.setDescription(description); + + return yesNoLabel; +} \ No newline at end of file diff --git a/src/utils/private-help-utils.ts b/src/utils/private-help-utils.ts index a41d148..4f45046 100644 --- a/src/utils/private-help-utils.ts +++ b/src/utils/private-help-utils.ts @@ -1,5 +1,6 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, LabelBuilder, ModalBuilder, PrivateThreadChannel, StringSelectMenuBuilder, StringSelectMenuOptionBuilder, TextChannel, TextInputBuilder, 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 { createTextInput, createYesNoMenu } from './modal-utils'; export async function closeOldTickets(client: Client) { for (const ticket of await Database.getAllOpenPrivateHelpTickets()) { @@ -89,46 +90,9 @@ export async function openModTicketModal(interaction: UserContextMenuCommandInte .setCustomId(`open-mod-ticket_${member.id}`) .setTitle(`Opening A Mod Ticket With ${member.displayName}`); - const titleInput = new TextInputBuilder() - .setCustomId('title') - .setMaxLength(100) - .setStyle(TextInputStyle.Short) - .setRequired(false); - - const titleLabel = new LabelBuilder() - .setLabel('Title') - .setDescription(`The name of the thread. Defaults to "Mod Ticket For ${member.displayName}" if left empty`) - .setTextInputComponent(titleInput); - - const initialMessageInput = new TextInputBuilder() - .setCustomId('initial-message') - .setMaxLength(1800) - .setStyle(TextInputStyle.Paragraph) - .setRequired(false); - - const initialMessageLabel = new LabelBuilder() - .setLabel('Inital Message') - .setDescription('The inital message sent in the thread') - .setTextInputComponent(initialMessageInput); - - const yesNoMenu = new StringSelectMenuBuilder() - .setCustomId('auto-join-thread') - .setRequired(false) - .addOptions( - new StringSelectMenuOptionBuilder() - .setLabel('Yes') - .setDefault(true) - .setValue('yes'), - new StringSelectMenuOptionBuilder() - .setLabel('No') - .setDefault(false) - .setValue('no') - ); - - const autoJoinLabel = new LabelBuilder() - .setLabel('Auto Join Thread') - .setDescription('Whether or not to join you to the thread. Selecting no will not notify you of messages sent!') - .setStringSelectMenuComponent(yesNoMenu); + 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 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); modal.addLabelComponents(titleLabel, initialMessageLabel, autoJoinLabel);