import { ActionRow, ApplicationCommandFlags, ApplicationCommandTypes, Bot, ButtonStyles, Interaction, InteractionResponseTypes, MessageComponentTypes, TextStyles } from '../../../deps.ts'; import { infoColor1, somethingWentWrong } from '../../commandUtils.ts'; import { CommandDetails } from '../../types/commandTypes.ts'; import { Activities } from './activities.ts'; import { addTokenToMap, deleteTokenEarly, generateActionRow, generateMapId, getNestedActivity, idSeparator, LfgEmbedIndexes, lfgStartTimeName, pathIdxEnder, pathIdxSeparator, selfDestructMessage, tokenMap, } from './utils.ts'; import utils from '../../utils.ts'; import { customId as createCustomActivityBtnId } from './step1a-openCustomModal.ts'; import { customId as finalizeEventBtnId } from './step2-finalize.ts'; import { monthsShort } from './dateTimeUtils.ts'; import { dbClient, queries } from '../../db.ts'; export const customId = 'gameSel'; export const eventTimeId = 'eventTime'; export const eventTimeZoneId = 'eventTimeZone'; export const eventDateId = 'eventDate'; export const eventDescriptionId = 'eventDescription'; const slashCommandName = 'create-event'; const details: CommandDetails = { name: slashCommandName, description: 'Creates a new event in this channel.', type: ApplicationCommandTypes.ChatInput, }; const customEventRow: ActionRow = { type: MessageComponentTypes.ActionRow, components: [{ type: MessageComponentTypes.Button, style: ButtonStyles.Primary, label: 'Create Custom Event', customId: createCustomActivityBtnId, }], }; const execute = async (bot: Bot, interaction: Interaction) => { if (interaction.data && (interaction.data.name === slashCommandName || interaction.data.customId) && interaction.member && interaction.guildId && interaction.channelId) { // Light Telemetry if (interaction.data.name === slashCommandName) { dbClient.execute(queries.callIncCnt('cmd-gameSel')).catch((e) => utils.commonLoggers.dbError('step1-gameSelection.ts@cmd', 'call sproc INC_CNT on', e)); } if (interaction.data.customId === customId) { dbClient.execute(queries.callIncCnt('btn-gameSel')).catch((e) => utils.commonLoggers.dbError('step1-gameSelection.ts@btn', 'call sproc INC_CNT on', e)); } // Check if we are done const customIdIdxPath = (interaction.data.customId || '').substring((interaction.data.customId || '').indexOf(idSeparator) + 1) || ''; const valuesIdxPath = interaction.data?.values?.[0] || ''; const strippedIdxPath = interaction.data.customId?.includes(idSeparator) ? customIdIdxPath : valuesIdxPath; const finalizedIdxPath = strippedIdxPath.substring(0, strippedIdxPath.lastIndexOf(pathIdxEnder)); if ((interaction.data.customId?.includes(idSeparator) && interaction.data.customId.endsWith(pathIdxEnder)) || interaction.data?.values?.[0].endsWith(pathIdxEnder)) { // User selected activity, give them the details modal and delete the selectMenus await deleteTokenEarly(bot, interaction, interaction.guildId, interaction.channelId, interaction.member.id); let prefillTime = ''; let prefillTimeZone = ''; let prefillDate = ''; let prefillDescription = ''; if (interaction.message && interaction.message.embeds[0].fields && interaction.message.embeds[0].fields[LfgEmbedIndexes.StartTime].name === lfgStartTimeName) { let rawEventDateTime = interaction.message.embeds[0].fields[LfgEmbedIndexes.StartTime].value.split('\n')[0].split(' '); const monthIdx = rawEventDateTime.findIndex((item) => monthsShort.includes(item.toUpperCase())); prefillTime = rawEventDateTime.slice(0, monthIdx - 1).join(' ').trim(); prefillTimeZone = rawEventDateTime[monthIdx - 1].trim(); prefillDate = rawEventDateTime.slice(monthIdx).join(' ').trim(); prefillDescription = interaction.message.embeds[0].fields[LfgEmbedIndexes.Description].value.trim(); } bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.Modal, data: { title: 'Enter Event Details', customId: `${finalizeEventBtnId}${idSeparator}${finalizedIdxPath}`, components: [{ type: MessageComponentTypes.ActionRow, components: [{ type: MessageComponentTypes.InputText, customId: eventTimeId, label: 'Start Time:', placeholder: 'Enter the start time as "HH:MM AM/PM"', style: TextStyles.Short, minLength: 1, maxLength: 8, value: prefillTime || undefined, }], }, { type: MessageComponentTypes.ActionRow, components: [{ type: MessageComponentTypes.InputText, customId: eventTimeZoneId, label: 'Time Zone:', placeholder: 'Enter your time zone abbreviation (UTC±## also works)', style: TextStyles.Short, minLength: 2, maxLength: 8, value: prefillTimeZone || undefined, }], }, { type: MessageComponentTypes.ActionRow, components: [{ type: MessageComponentTypes.InputText, customId: eventDateId, label: 'Start Date:', placeholder: 'Enter date as "MONTH/DAY/YEAR" or "Month Day, Year"', style: TextStyles.Short, minLength: 1, maxLength: 20, value: prefillDate || undefined, }], }, { type: MessageComponentTypes.ActionRow, components: [{ type: MessageComponentTypes.InputText, customId: eventDescriptionId, label: 'Description:', placeholder: 'Briefly describe the event', style: TextStyles.Paragraph, required: false, minLength: 0, maxLength: 1000, value: prefillDescription || undefined, }], }], }, }).catch((e: Error) => utils.commonLoggers.interactionSendError('step1-gameSelection.ts:modal', interaction, e)); return; } // Parse indexPath from the select value const rawIdxPath: Array = interaction.data.values ? interaction.data.values[0].split(pathIdxSeparator) : ['']; const idxPath: Array = rawIdxPath.map((rawIdx) => rawIdx ? parseInt(rawIdx) : -1); const selectMenus: Array = []; let selectMenuCustomId = `${customId}$`; let currentBaseValue = ''; for (let i = 0; i < idxPath.length; i++) { const idx = idxPath[i]; const idxPathCopy = [...idxPath].slice(0, i); selectMenus.push(generateActionRow(currentBaseValue, getNestedActivity(idxPathCopy, Activities), selectMenuCustomId, idx)); selectMenuCustomId = `${selectMenuCustomId}$`; currentBaseValue = `${currentBaseValue}${idx}${pathIdxSeparator}`; } selectMenus.push(customEventRow); if (interaction.data.customId && interaction.data.customId.includes('$')) { // Let discord know we didn't ignore the user await bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.DeferredUpdateMessage, }).catch((e: Error) => utils.commonLoggers.interactionSendError('step1-gameSelection.ts:ping', interaction, e)); // Update the original game selector await bot.helpers.editOriginalInteractionResponse(tokenMap.get(generateMapId(interaction.guildId, interaction.channelId, interaction.member.id))?.token || '', { components: selectMenus, }).catch((e: Error) => utils.commonLoggers.interactionSendError('step1-gameSelection.ts:edit', interaction, e)); } else { // Delete old token entry if it exists await deleteTokenEarly(bot, interaction, interaction.guildId, interaction.channelId, interaction.member.id); // Store token for later use addTokenToMap(bot, interaction, interaction.guildId, interaction.channelId, interaction.member.id); // Send initial interaction bot.helpers.sendInteractionResponse(interaction.id, interaction.token, { type: InteractionResponseTypes.ChannelMessageWithSource, data: { embeds: [{ title: 'Please select a Game and Activity, or create a Custom Event.', description: selfDestructMessage(new Date().getTime()), color: infoColor1, }], flags: ApplicationCommandFlags.Ephemeral, components: selectMenus, }, }).catch((e: Error) => utils.commonLoggers.interactionSendError('step1-gameSelection.ts:init', interaction, e)); } } else { somethingWentWrong(bot, interaction, 'missingCoreValuesOnGameSel'); } }; export const gameSelectionCommand = { details, execute, }; export const gameSelectionButton = { customId, execute, };