Add global command channel rules and enhance command handling

- Introduced global command channel whitelist and blacklist in the GuildSettings model for improved command access control.
- Implemented command gate logic in the command routing to handle channel and role restrictions, providing user feedback for denied commands.
- Enhanced the CommandsManager component to support global rules and channel/role selection, improving the user interface for managing command permissions.
- Updated localization files to include new keys for global command rules and related messages, ensuring clarity in user interactions.
This commit is contained in:
TheOnlyMace
2026-07-22 20:26:40 +02:00
parent 0fb8195236
commit d31660f813
24 changed files with 963 additions and 185 deletions

View File

@@ -4,10 +4,11 @@ import {
type ChatInputCommandInteraction,
type MessageContextMenuCommandInteraction
} from 'discord.js';
import { t } from '@nexumi/shared';
import { t, tf, type Locale } from '@nexumi/shared';
import { env } from './env.js';
import { logger } from './logger.js';
import { getGuildLocale } from './i18n.js';
import { evaluateCommandGate, type CommandGateReason } from './command-gates.js';
import { isMaintenanceMode } from './presence.js';
import { moderationCommands } from './modules/moderation/commands.js';
import { automodCommands } from './modules/automod/commands.js';
@@ -95,6 +96,25 @@ export async function registerCommands() {
);
}
function gateMessage(locale: Locale, reason: CommandGateReason, retryAfterSeconds?: number): string {
switch (reason) {
case 'disabled':
return t(locale, 'generic.commandDisabled');
case 'channel_denied':
case 'channel_not_allowed':
return t(locale, 'generic.commandChannelBlocked');
case 'role_denied':
case 'role_not_allowed':
return t(locale, 'generic.commandRoleBlocked');
case 'cooldown':
return tf(locale, 'generic.commandCooldown', {
seconds: retryAfterSeconds ?? 1
});
default:
return t(locale, 'generic.noPermission');
}
}
export async function routeCommand(interaction: ChatInputCommandInteraction, context: BotContext) {
const locale = await getGuildLocale(context.prisma, interaction.guildId);
const maintenance = await isMaintenanceMode(context);
@@ -112,6 +132,16 @@ export async function routeCommand(interaction: ChatInputCommandInteraction, con
await interaction.reply({ content: t(locale, 'generic.unknownCommand'), ephemeral: true });
return;
}
const gate = await evaluateCommandGate(interaction, context);
if (!gate.ok) {
await interaction.reply({
content: gateMessage(locale, gate.reason, gate.retryAfterSeconds),
ephemeral: true
});
return;
}
await command.execute(interaction, context);
}