Add editActivity system to change preset or custom activity

This commit is contained in:
Ean Milligan (Bastion) 2023-04-27 01:47:33 -04:00
parent 12bb1f614e
commit 2edcd3a961
6 changed files with 241 additions and 2 deletions

View File

@ -27,6 +27,7 @@ const actions = [
'btn-confirmDelEvent',
'btn-editEvent',
'btn-eeChangeAct',
'btn-eeCustomAct',
'btn-eeChangeTime',
'btn-eeChangeDesc',
'btn-eeMakePublic',

View File

@ -18,6 +18,8 @@ import { applyDescriptionButton } from './live-event/applyDescription.ts';
import { applyDateTimeButton } from './live-event/applyDateTime.ts';
import { updateEventButton } from './live-event/updateEvent.ts';
import { toggleWLStatusButton } from './live-event/toggleWLStatus.ts';
import { editActivityButton } from './live-event/editActivity.ts';
import { editActivityCustomButton } from './live-event/editActivity-custom.ts';
export const buttons: Array<Button> = [
gameSelectionButton,
@ -39,4 +41,6 @@ export const buttons: Array<Button> = [
applyDateTimeButton,
updateEventButton,
toggleWLStatusButton,
editActivityButton,
editActivityCustomButton,
];

View File

@ -0,0 +1,31 @@
import { Bot, Interaction, InteractionResponseTypes } from '../../../deps.ts';
import { deleteTokenEarly } from '../tokenCleanup.ts';
import { generateCustomActivityFields, idSeparator } from '../eventUtils.ts';
import { customId as editActivityCustomId } from './editActivity.ts';
import utils from '../../utils.ts';
import { dbClient, queries } from '../../db.ts';
export const customId = 'editActivityCustom';
const execute = async (bot: Bot, interaction: Interaction) => {
if (interaction.data?.customId && interaction.member && interaction.guildId && interaction.channelId) {
// Light Telemetry
dbClient.execute(queries.callIncCnt('btn-eeCustomAct')).catch((e) => utils.commonLoggers.dbError('step1a-openCustomModal.ts', 'call sproc INC_CNT on', e));
deleteTokenEarly(bot, interaction, interaction.guildId, interaction.channelId, interaction.member.id);
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.Modal,
data: {
title: 'Create Custom Activity',
customId: `${editActivityCustomId}${idSeparator}${interaction.data.customId.split(idSeparator)[1] || ''}`,
components: generateCustomActivityFields(),
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('editActivity-custom.ts', interaction, e));
}
};
export const editActivityCustomButton = {
customId,
execute,
};

View File

@ -0,0 +1,202 @@
import { ActionRow, ApplicationCommandFlags, Bot, ButtonStyles, Interaction, InteractionResponseTypes, MessageComponentTypes } from '../../../deps.ts';
import { failColor, infoColor1, safelyDismissMsg, somethingWentWrong } from '../../commandUtils.ts';
import { Activities, Activity } from '../event-creation/activities.ts';
import { generateActionRow, getFinalActivity, getNestedActivity } from '../event-creation/utils.ts';
import { activityMaxPlayersId, activitySubtitleId, activityTitleId, fillerChar, generateAlternateList, generateMemberList, generateMemberTitle, idSeparator, LfgEmbedIndexes, pathIdxEnder, pathIdxSeparator } from '../eventUtils.ts';
import { addTokenToMap, deleteTokenEarly, generateMapId, selfDestructMessage, tokenMap } from '../tokenCleanup.ts';
import utils from '../../utils.ts';
import config from '../../../config.ts';
import { dbClient, queries } from '../../db.ts';
import { customId as editActivityCustomCustomId } from './editActivity-custom.ts';
import { applyEditButtons, applyEditMessage, getEventMemberCount, getLfgMembers } from './utils.ts';
import { LFGMember } from '../../types/commandTypes.ts';
export const customId = 'editActivity';
const makeCustomEventRow = (customIdIdxPath: string): ActionRow => ({
type: MessageComponentTypes.ActionRow,
components: [{
type: MessageComponentTypes.Button,
style: ButtonStyles.Primary,
label: 'Create Custom Event',
customId: `${editActivityCustomCustomId}${idSeparator}${customIdIdxPath}`,
}],
});
const execute = async (bot: Bot, interaction: Interaction) => {
if (interaction.data?.customId && interaction.member && interaction.guildId && interaction.channelId) {
const [evtChannelId, evtMessageId] = (interaction.data.customId.replaceAll(pathIdxEnder, '').replaceAll(fillerChar, '').split(idSeparator)[1] || '').split(pathIdxSeparator).map((id) => BigInt(id || '0'));
// Check if we are done
const valuesIdxPath = interaction.data?.values?.[0] || '';
const finalizedIdxPath = valuesIdxPath.substring(0, valuesIdxPath.lastIndexOf(pathIdxEnder));
if (interaction.data?.values?.[0].endsWith(pathIdxEnder) || (interaction.data?.components && interaction.data.components.length > 0)) {
// User selected activity, give them the confirmation message and delete the selectMenus
await deleteTokenEarly(bot, interaction, interaction.guildId, interaction.channelId, interaction.member.id);
// Fill in the activity details
let selectedCategory = '';
let selectedActivity: Activity = {
name: '',
maxMembers: 0,
};
if (interaction.data.components) {
// Parse out our data
const tempDataMap: Map<string, string> = new Map();
for (const row of interaction.data.components) {
if (row.components?.[0]) {
const textField = row.components[0];
tempDataMap.set(textField.customId || 'missingCustomId', textField.value || '');
}
}
// Remove any pipe characters to avoid issues down the process
const activityTitle = (tempDataMap.get(activityTitleId) || '').replace(/\|/g, '');
const activitySubtitle = (tempDataMap.get(activitySubtitleId) || '').replace(/\|/g, '');
const activityMaxPlayers = parseInt(tempDataMap.get(activityMaxPlayersId) || '0');
if (isNaN(activityMaxPlayers) || activityMaxPlayers < 1 || activityMaxPlayers > 99) {
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
embeds: [{
color: failColor,
title: 'Invalid Max Member count!',
description: `${config.name} parsed the max members as \`${isNaN(activityMaxPlayers) ? 'Not a Number' : activityMaxPlayers
}\`, which is outside of the allowed range. Please re-edit this activity, but make sure the maximum player count is between 1 and 99.\n\n${safelyDismissMsg}`,
}],
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('editActivity.ts@invalidPlayer', interaction, e));
return;
}
if (!activityMaxPlayers || !activitySubtitle || !activityTitle) {
// Verify fields exist
somethingWentWrong(bot, interaction, `missingFieldFromEditCustomActivity@${activityTitle}|${activitySubtitle}|${activityMaxPlayers}$`);
return;
}
selectedCategory = activityTitle;
selectedActivity.name = activitySubtitle;
selectedActivity.maxMembers = activityMaxPlayers;
} else {
const rawIdxPath: Array<string> = finalizedIdxPath.split(pathIdxSeparator);
const idxPath: Array<number> = rawIdxPath.map((rawIdx) => rawIdx ? parseInt(rawIdx) : -1);
selectedCategory = Activities[idxPath[0]].name;
selectedActivity = getFinalActivity(idxPath, Activities);
}
if (
!selectedActivity.maxMembers || !selectedCategory || !selectedActivity.name || (isNaN(selectedActivity.maxMembers) || (selectedActivity.maxMembers < 1 || selectedActivity.maxMembers > 99))
) {
// Verify fields exist
somethingWentWrong(bot, interaction, `parseFailInEditCustomActivity@${selectedCategory}|${selectedActivity.name}|${selectedActivity.maxMembers || 'undefined'}$`);
return;
}
// Get event to apply edit
const eventMessage = await bot.helpers.getMessage(evtChannelId, evtMessageId).catch((e: Error) => utils.commonLoggers.messageGetError('editActivity.ts', 'get eventMessage', e));
if (eventMessage && eventMessage.embeds[0].fields) {
// Update member lists
const [currentMemberCount, _oldMaxMemberCount] = getEventMemberCount(eventMessage.embeds[0].fields[LfgEmbedIndexes.JoinedMembers].name);
const currentMembers = getLfgMembers(eventMessage.embeds[0].fields[LfgEmbedIndexes.JoinedMembers].value);
const currentAlternates = getLfgMembers(eventMessage.embeds[0].fields[LfgEmbedIndexes.AlternateMembers].value);
if (currentMemberCount > selectedActivity.maxMembers) {
const membersToAlternate = currentMembers.splice(selectedActivity.maxMembers).map((member): LFGMember => ({
id: member.id,
name: member.name,
joined: true,
}));
currentAlternates.unshift(...membersToAlternate);
}
// Apply edits
eventMessage.embeds[0].fields[LfgEmbedIndexes.Activity].name = `${selectedCategory}:`;
eventMessage.embeds[0].fields[LfgEmbedIndexes.Activity].value = selectedActivity.name;
eventMessage.embeds[0].fields[LfgEmbedIndexes.JoinedMembers].name = generateMemberTitle(currentMembers, selectedActivity.maxMembers);
eventMessage.embeds[0].fields[LfgEmbedIndexes.JoinedMembers].value = generateMemberList(currentMembers);
eventMessage.embeds[0].fields[LfgEmbedIndexes.AlternateMembers].value = generateAlternateList(currentAlternates);
// Send edit confirmation
addTokenToMap(bot, interaction, interaction.guildId, interaction.channelId, interaction.member.id);
bot.helpers.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ChannelMessageWithSource,
data: {
flags: ApplicationCommandFlags.Ephemeral,
content: applyEditMessage(new Date().getTime()),
embeds: [eventMessage.embeds[0]],
components: applyEditButtons(interaction.data.customId.replaceAll(fillerChar, '').split(idSeparator)[1] || ''),
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('editActivity.ts', interaction, e));
} else {
somethingWentWrong(bot, interaction, 'failedToGetEventMsgInEditActivity');
}
return;
}
// Parse indexPath from the select value
const rawIdxPath: Array<string> = interaction.data.values ? interaction.data.values[0].split(pathIdxSeparator) : [''];
const idxPath: Array<number> = rawIdxPath.map((rawIdx) => rawIdx ? parseInt(rawIdx) : -1);
const selectMenus: Array<ActionRow> = [];
// Use fillerChar to create unique customIds for dropdowns
// We also leverage this to determine if its the first time the user has entered gameSel
let selectMenuCustomId = `${interaction.data.customId.replaceAll(fillerChar, '')}${fillerChar}`;
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}${fillerChar}`;
currentBaseValue = `${currentBaseValue}${idx}${pathIdxSeparator}`;
}
selectMenus.push(makeCustomEventRow(interaction.data.customId.replaceAll(fillerChar, '').split(idSeparator)[1] || ''));
if (interaction.data.customId && interaction.data.customId.includes(fillerChar)) {
// 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('editActivity.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('editActivity.ts@edit', interaction, e));
} else {
// Light Telemetry (placed here so it only gets called on first run)
dbClient.execute(queries.callIncCnt('btn-eeChangeAct')).catch((e) => utils.commonLoggers.dbError('editActivity.ts', 'call sproc INC_CNT on', e));
// 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: `Changing activity for [this event](${utils.idsToMessageUrl({
guildId: interaction.guildId,
channelId: evtChannelId,
messageId: evtMessageId,
})
}).\n\n${selfDestructMessage(new Date().getTime())}`,
color: infoColor1,
}],
flags: ApplicationCommandFlags.Ephemeral,
components: selectMenus,
},
}).catch((e: Error) => utils.commonLoggers.interactionSendError('editActivity.ts@init', interaction, e));
}
} else {
somethingWentWrong(bot, interaction, 'missingCoreValuesOnEditActivity');
}
};
export const editActivityButton = {
customId,
execute,
};

View File

@ -4,6 +4,7 @@ import { infoColor1, somethingWentWrong, stopThat } from '../../commandUtils.ts'
import { idSeparator, pathIdxEnder, pathIdxSeparator } from '../eventUtils.ts';
import { addTokenToMap, selfDestructMessage } from '../tokenCleanup.ts';
import utils from '../../utils.ts';
import { customId as editActivityCustomId } from './editActivity.ts';
import { customId as editDescriptionCustomId } from './editDescription.ts';
import { customId as editDateTimeCustomId } from './editDateTime.ts';
import { customId as toggleWLStatusCustomId } from './toggleWLStatus.ts';
@ -41,7 +42,7 @@ const execute = async (bot: Bot, interaction: Interaction) => {
type: MessageComponentTypes.Button,
label: 'Change Activity',
style: ButtonStyles.Primary,
customId: `a${editIdxPath}`, // TODO: add customId
customId: `${editActivityCustomId}${editIdxPath}`,
}, {
type: MessageComponentTypes.Button,
label: 'Change Date/Time',

View File

@ -51,7 +51,7 @@ setInterval(() => {
}, oneHour);
// Get Member Counts from the title
const getEventMemberCount = (rawMemberTitle: string): [number, number] => {
export const getEventMemberCount = (rawMemberTitle: string): [number, number] => {
const [rawCurrentCount, rawMaxCount] = rawMemberTitle.split('/');
const currentMemberCount = parseInt(rawCurrentCount.split(':')[1] || '0');
const maxMemberCount = parseInt(rawMaxCount || '0');