Files
Nexumi/apps/bot/src/interaction-registry.ts
TheOnlyMace 5f932f5d63 Refactor ephemeral message handling in ticket interactions
- Updated ticket command and interaction responses to utilize the new `ephemeral()` function for consistent ephemeral message handling.
- Enhanced error handling for member fetching in ticket interactions, ensuring proper responses when members are not found.
- Improved localization for new error messages related to member presence in ticket commands.
- Documented changes in phase tracking to reflect updates in ticket interaction responses.
2026-07-25 16:32:30 +02:00

217 lines
6.3 KiB
TypeScript

import type {
ButtonInteraction,
ChannelSelectMenuInteraction,
MentionableSelectMenuInteraction,
ModalSubmitInteraction,
RoleSelectMenuInteraction,
StringSelectMenuInteraction,
UserSelectMenuInteraction
} from 'discord.js';
import { decodeComponentCustomId, t, type DashboardModuleId } from '@nexumi/shared';
import { env } from './env.js';
import { getGuildLocale } from './i18n.js';
import { ephemeral } from './interaction-reply.js';
import { isModuleEnabled, moduleForComponent } from './module-gates.js';
import { isMaintenanceMode } from './presence.js';
import type { BotContext } from './types.js';
import {
handleModerationConfirmation,
isModerationConfirmation
} from './modules/moderation/confirmations.js';
import { handleVerificationButton } from './modules/verification/handlers.js';
import { isVerificationButton } from './modules/verification/service.js';
import {
handleBlackjackButton,
isBlackjackButton
} from './modules/economy/blackjack-buttons.js';
import {
handleEmbedModal,
handlePollButton,
isEmbedModal,
isPollButton
} from './modules/utility/index.js';
import { handleFunButton, isFunButton } from './modules/fun/index.js';
import { handleGiveawayButton, isGiveawayButton } from './modules/giveaways/index.js';
import { handleTicketInteraction, isTicketInteraction } from './modules/tickets/index.js';
import {
handleSelfRoleInteraction,
isSelfRoleInteraction
} from './modules/selfroles/index.js';
import {
handleSuggestionButton,
isSuggestionButton
} from './modules/suggestions/index.js';
import {
handleTempVoiceInteraction,
isTempVoiceInteraction,
isTempVoiceModal
} from './modules/tempvoice/index.js';
import { handleGuildBackupButton, isGuildBackupButton } from './modules/guildbackup/index.js';
import {
handleComponentsV2Interaction,
isComponentsV2Interaction
} from './modules/components-v2/index.js';
type AnyComponentInteraction =
| ButtonInteraction
| StringSelectMenuInteraction
| UserSelectMenuInteraction
| RoleSelectMenuInteraction
| ChannelSelectMenuInteraction
| MentionableSelectMenuInteraction
| ModalSubmitInteraction;
type InteractionHandler = {
match: (customId: string) => boolean;
handle: (interaction: AnyComponentInteraction, context: BotContext) => Promise<void>;
};
function asButton(interaction: AnyComponentInteraction): ButtonInteraction {
return interaction as ButtonInteraction;
}
function asModal(interaction: AnyComponentInteraction): ModalSubmitInteraction {
return interaction as ModalSubmitInteraction;
}
const handlers: InteractionHandler[] = [
{
match: isModerationConfirmation,
handle: (interaction, context) => handleModerationConfirmation(asButton(interaction), context)
},
{
match: isVerificationButton,
handle: (interaction, context) => handleVerificationButton(asButton(interaction), context)
},
{
match: isBlackjackButton,
handle: (interaction, context) => handleBlackjackButton(asButton(interaction), context)
},
{
match: isPollButton,
handle: (interaction, context) => handlePollButton(asButton(interaction), context)
},
{
match: isFunButton,
handle: (interaction, context) => handleFunButton(asButton(interaction), context)
},
{
match: isGiveawayButton,
handle: (interaction, context) => handleGiveawayButton(asButton(interaction), context)
},
{
match: isTicketInteraction,
handle: (interaction, context) =>
handleTicketInteraction(
interaction as Parameters<typeof handleTicketInteraction>[0],
context
)
},
{
match: isSelfRoleInteraction,
handle: (interaction, context) =>
handleSelfRoleInteraction(
interaction as Parameters<typeof handleSelfRoleInteraction>[0],
context
)
},
{
match: isSuggestionButton,
handle: (interaction, context) => handleSuggestionButton(asButton(interaction), context)
},
{
match: (customId) => isTempVoiceInteraction(customId) || isTempVoiceModal(customId),
handle: (interaction, context) =>
handleTempVoiceInteraction(
interaction as Parameters<typeof handleTempVoiceInteraction>[0],
context
)
},
{
match: isGuildBackupButton,
handle: (interaction, context) => handleGuildBackupButton(asButton(interaction), context)
},
{
match: isEmbedModal,
handle: (interaction, context) => handleEmbedModal(asModal(interaction), context)
},
{
match: isComponentsV2Interaction,
handle: (interaction, context) =>
handleComponentsV2Interaction(
interaction as Parameters<typeof handleComponentsV2Interaction>[0],
context
)
}
];
function moduleForComponentsV2(customId: string): DashboardModuleId | null {
const decoded = decodeComponentCustomId(customId);
if (!decoded) {
return null;
}
switch (decoded.source) {
case 'w':
return 'welcome';
case 't':
return 'tags';
case 's':
return 'scheduler';
case 'm':
return 'messages';
default:
return 'messages';
}
}
export async function routeComponentInteraction(
interaction: AnyComponentInteraction,
context: BotContext
): Promise<boolean> {
const customId = interaction.customId;
const locale = await getGuildLocale(context.prisma, interaction.guildId);
const maintenance = await isMaintenanceMode(context);
const ownerIds = env.OWNER_USER_IDS ?? [];
if (maintenance.enabled && !ownerIds.includes(interaction.user.id)) {
if (!interaction.replied && !interaction.deferred) {
await interaction.reply({
content: maintenance.message?.trim() || t(locale, 'generic.maintenance'),
...ephemeral()
});
}
return true;
}
let moduleId = moduleForComponent(customId);
if (!moduleId && isComponentsV2Interaction(customId)) {
moduleId = moduleForComponentsV2(customId);
}
if (moduleId && interaction.guildId) {
const enabled = await isModuleEnabled(
context.prisma,
context.redis,
interaction.guildId,
moduleId
);
if (!enabled) {
if (!interaction.replied && !interaction.deferred) {
await interaction.reply({
content: t(locale, 'generic.moduleDisabled'),
...ephemeral()
});
}
return true;
}
}
for (const handler of handlers) {
if (handler.match(customId)) {
await handler.handle(interaction, context);
return true;
}
}
return false;
}