204 lines
6.8 KiB
TypeScript
204 lines
6.8 KiB
TypeScript
import config from '../../../config.ts';
|
|
import { Activity } from './activities.ts';
|
|
import {
|
|
ActionRow,
|
|
ApplicationCommandFlags,
|
|
Bot,
|
|
ButtonComponent,
|
|
ButtonStyles,
|
|
Interaction,
|
|
InteractionResponse,
|
|
InteractionResponseTypes,
|
|
MessageComponentTypes,
|
|
SelectOption,
|
|
} from '../../../deps.ts';
|
|
import utils from '../../utils.ts';
|
|
import { successColor } from '../../commandUtils.ts';
|
|
import { LFGMember } from '../../types/commandTypes.ts';
|
|
import { customId as gameSelCustomId } from './step1-gameSelection.ts';
|
|
|
|
// Discord Interaction Tokens last 15 minutes, we will self kill after 14.5 minutes
|
|
const tokenTimeoutS = (15 * 60) - 30;
|
|
export const tokenTimeoutMS = tokenTimeoutS * 1000;
|
|
export const idSeparator = '@';
|
|
export const pathIdxSeparator = '|';
|
|
export const pathIdxEnder = '&';
|
|
export const selfDestructMessage = (currentTime: number) =>
|
|
`**Please note:** This message will self destruct <t:${Math.floor((currentTime + tokenTimeoutMS) / 1000)}:R> due to limits imposed by the Discord API.`;
|
|
|
|
export const tokenMap: Map<string, {
|
|
token: string;
|
|
timeoutId: number;
|
|
}> = new Map();
|
|
|
|
export const getNestedActivity = (idxPath: Array<number>, activities: Array<Activity>): Array<Activity> => {
|
|
const nextIdx = idxPath[0];
|
|
if (idxPath.length && activities[nextIdx] && activities[nextIdx].options) {
|
|
idxPath.shift();
|
|
return getNestedActivity(idxPath, activities[nextIdx].options || []);
|
|
} else {
|
|
return activities;
|
|
}
|
|
};
|
|
|
|
export const getFinalActivity = (idxPath: Array<number>, activities: Array<Activity>): Activity => getNestedActivity(idxPath, activities)[idxPath[idxPath.length - 1]];
|
|
|
|
const getSelectOptions = (baseValue: string, activities: Array<Activity>, defaultIdx?: number): Array<SelectOption> =>
|
|
activities.map((act, idx) => ({
|
|
label: act.name,
|
|
value: `${baseValue}${idx}${act.maxMembers ? pathIdxEnder : pathIdxSeparator}`,
|
|
default: idx === defaultIdx,
|
|
}));
|
|
|
|
export const generateActionRow = (baseValue: string, activities: Array<Activity>, customId: string, defaultIdx?: number): ActionRow => ({
|
|
type: MessageComponentTypes.ActionRow,
|
|
components: [{
|
|
type: MessageComponentTypes.SelectMenu,
|
|
customId,
|
|
options: getSelectOptions(baseValue, activities, defaultIdx),
|
|
}],
|
|
});
|
|
|
|
export const generateMapId = (guildId: bigint, channelId: bigint, userId: bigint) => `${guildId}-${channelId}-${userId}`;
|
|
|
|
export const addTokenToMap = (bot: Bot, interaction: Interaction, guildId: bigint, channelId: bigint, userId: bigint) =>
|
|
tokenMap.set(generateMapId(guildId, channelId, userId), {
|
|
token: interaction.token,
|
|
timeoutId: setTimeout(
|
|
(guildId, channelId, userId) => {
|
|
deleteTokenEarly(bot, interaction, guildId, channelId, userId);
|
|
},
|
|
tokenTimeoutMS,
|
|
guildId,
|
|
channelId,
|
|
userId,
|
|
),
|
|
});
|
|
|
|
export const deleteTokenEarly = async (bot: Bot, interaction: Interaction, guildId: bigint, channelId: bigint, userId: bigint) => {
|
|
const tokenMapEntry = tokenMap.get(generateMapId(guildId, channelId, userId));
|
|
if (tokenMapEntry && tokenMapEntry.token) {
|
|
await bot.helpers.deleteOriginalInteractionResponse(tokenMap.get(generateMapId(guildId, channelId, userId))?.token || '').catch((e: Error) =>
|
|
utils.commonLoggers.interactionSendError('utils.ts:deleteTokenEarly', interaction, e)
|
|
);
|
|
clearTimeout(tokenMapEntry.timeoutId);
|
|
tokenMap.delete(generateMapId(guildId, channelId, userId));
|
|
}
|
|
};
|
|
|
|
const finalizeButtons = (idxPath: string): [ButtonComponent, ButtonComponent, ButtonComponent] => [{
|
|
type: MessageComponentTypes.Button,
|
|
label: 'Create Event',
|
|
style: ButtonStyles.Success,
|
|
customId: 'createEvent', // TODO: replace with proper id
|
|
}, {
|
|
type: MessageComponentTypes.Button,
|
|
label: 'Create Whitelisted Event',
|
|
style: ButtonStyles.Primary,
|
|
customId: `createEvent${idSeparator}`, // TODO: replace with proper id
|
|
}, {
|
|
type: MessageComponentTypes.Button,
|
|
label: 'Edit Event Details',
|
|
style: ButtonStyles.Secondary,
|
|
customId: `${gameSelCustomId}${idSeparator}${idxPath}${pathIdxEnder}`,
|
|
}];
|
|
|
|
export const generateLFGButtons = (whitelist: boolean): [ButtonComponent, ButtonComponent, ButtonComponent, ButtonComponent, ButtonComponent] => [{
|
|
type: MessageComponentTypes.Button,
|
|
label: `${whitelist ? 'Request to ' : ''}Join`,
|
|
style: ButtonStyles.Success,
|
|
customId: `joinEvent${whitelist ? idSeparator : ''}`, // TODO: replace with proper id
|
|
}, {
|
|
type: MessageComponentTypes.Button,
|
|
label: `Join as Alternate`,
|
|
style: ButtonStyles.Primary,
|
|
customId: 'alternateEvent', // TODO: replace with proper id
|
|
}, {
|
|
type: MessageComponentTypes.Button,
|
|
label: 'Leave',
|
|
style: ButtonStyles.Danger,
|
|
customId: 'leaveEvent', // TODO: replace with proper id
|
|
}, {
|
|
type: MessageComponentTypes.Button,
|
|
label: '',
|
|
style: ButtonStyles.Secondary,
|
|
customId: 'editEvent', // TODO: replace with proper id
|
|
emoji: {
|
|
name: '✏️',
|
|
},
|
|
}, {
|
|
type: MessageComponentTypes.Button,
|
|
label: '',
|
|
style: ButtonStyles.Secondary,
|
|
customId: 'deleteEvent', // TODO: replace with proper id
|
|
emoji: {
|
|
name: '🗑️',
|
|
},
|
|
}];
|
|
|
|
export enum LfgEmbedIndexes {
|
|
Activity,
|
|
StartTime,
|
|
ICSLink,
|
|
Description,
|
|
JoinedMembers,
|
|
AlternateMembers,
|
|
}
|
|
export const createLFGPost = (
|
|
category: string,
|
|
activity: Activity,
|
|
eventDateTime: Date,
|
|
eventDateTimeStr: String,
|
|
eventDescription: string,
|
|
author: string,
|
|
memberList: Array<LFGMember>,
|
|
alternateList: Array<LFGMember>,
|
|
idxPath: string,
|
|
editing: boolean,
|
|
whitelist = false,
|
|
): InteractionResponse => {
|
|
const icsDetails = `${category}: ${activity.name}`;
|
|
return {
|
|
type: InteractionResponseTypes.ChannelMessageWithSource,
|
|
data: {
|
|
flags: ApplicationCommandFlags.Ephemeral,
|
|
content: editing ? 'Please verify the information below, then click on the $name button below' : 'test',
|
|
embeds: [{
|
|
color: successColor,
|
|
fields: [{
|
|
name: `${category}:`,
|
|
value: activity.name,
|
|
inline: true,
|
|
}, {
|
|
name: 'Start Time:',
|
|
value: `${eventDateTimeStr}\n<t:${Math.floor(eventDateTime.getTime() / 1000)}:R>`,
|
|
inline: true,
|
|
}, {
|
|
name: 'Add to Calendar:',
|
|
value: `[Download ICS File](${config.links.addToCalendar}?t=${eventDateTime.getTime()}&n=${icsDetails.replaceAll(' ', '+')})`,
|
|
inline: true,
|
|
}, {
|
|
name: 'Description:',
|
|
value: eventDescription,
|
|
}, {
|
|
name: `Members Joined: ${memberList.length}/${activity.maxMembers}`,
|
|
value: memberList.length ? memberList.map((member) => `${member.name} - <@${member.id}>`).join('\n') : 'None',
|
|
inline: true,
|
|
}, {
|
|
name: 'Alternates:',
|
|
value: alternateList.length ? alternateList.map((member) => `${member.name} - <@${member.id}>${member.joined ? ' *' : ''}`).join('\n') : 'None',
|
|
inline: true,
|
|
}],
|
|
footer: {
|
|
text: `Created by: ${author}`,
|
|
},
|
|
timestamp: eventDateTime.getTime(),
|
|
}],
|
|
components: [{
|
|
type: MessageComponentTypes.ActionRow,
|
|
components: editing ? finalizeButtons(idxPath) : generateLFGButtons(whitelist),
|
|
}],
|
|
},
|
|
};
|
|
};
|