Enhance bot functionality with new features and improvements
- Updated package dependencies to include `@sentry/node` for error tracking. - Refactored command routing to incorporate user and guild blacklisting checks, improving command execution safety. - Enhanced health server metrics to include queue waiting times, providing better insights into system performance. - Introduced confirmation handling for guild backup commands, streamlining user interactions. - Improved moderation commands with better error handling and case management, ensuring robust moderation capabilities. - Added new duration parsing functions to handle timeout durations effectively, enhancing moderation features. - Updated localization files to reflect new command structures and user guidance improvements.
This commit is contained in:
@@ -9,7 +9,15 @@ import { env } from './env.js';
|
||||
import { logger } from './logger.js';
|
||||
import { getGuildLocale } from './i18n.js';
|
||||
import { evaluateCommandGate, type CommandGateReason } from './command-gates.js';
|
||||
import {
|
||||
isGuildBlacklisted,
|
||||
isModuleEnabled,
|
||||
isUserBlacklisted,
|
||||
moduleForCommand
|
||||
} from './module-gates.js';
|
||||
import { isMaintenanceMode } from './presence.js';
|
||||
import { recordCommandMetric } from './metrics.js';
|
||||
import { captureException } from './sentry.js';
|
||||
import { moderationCommands } from './modules/moderation/commands.js';
|
||||
import { automodCommands } from './modules/automod/commands.js';
|
||||
import { welcomeCommands } from './modules/welcome/commands.js';
|
||||
@@ -116,41 +124,109 @@ function gateMessage(locale: Locale, reason: CommandGateReason, retryAfterSecond
|
||||
}
|
||||
|
||||
export async function routeCommand(interaction: ChatInputCommandInteraction, context: BotContext) {
|
||||
const started = Date.now();
|
||||
let ok = true;
|
||||
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)) {
|
||||
await interaction.reply({
|
||||
content: maintenance.message?.trim() || t(locale, 'generic.maintenance'),
|
||||
ephemeral: true
|
||||
|
||||
try {
|
||||
if (await isUserBlacklisted(context.prisma, interaction.user.id)) {
|
||||
await interaction.reply({
|
||||
content: t(locale, 'generic.userBlacklisted'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.guildId && (await isGuildBlacklisted(context.prisma, interaction.guildId))) {
|
||||
await interaction.reply({
|
||||
content: t(locale, 'generic.guildBlacklisted'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const maintenance = await isMaintenanceMode(context);
|
||||
const ownerIds = env.OWNER_USER_IDS ?? [];
|
||||
if (maintenance.enabled && !ownerIds.includes(interaction.user.id)) {
|
||||
await interaction.reply({
|
||||
content: maintenance.message?.trim() || t(locale, 'generic.maintenance'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const command = map.get(interaction.commandName);
|
||||
if (!command) {
|
||||
await interaction.reply({ content: t(locale, 'generic.unknownCommand'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
const moduleId = moduleForCommand(interaction.commandName);
|
||||
if (moduleId && interaction.guildId) {
|
||||
const enabled = await isModuleEnabled(
|
||||
context.prisma,
|
||||
context.redis,
|
||||
interaction.guildId,
|
||||
moduleId
|
||||
);
|
||||
if (!enabled) {
|
||||
await interaction.reply({
|
||||
content: t(locale, 'generic.moduleDisabled'),
|
||||
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);
|
||||
} catch (error) {
|
||||
ok = false;
|
||||
captureException(error, {
|
||||
command: interaction.commandName,
|
||||
guildId: interaction.guildId,
|
||||
userId: interaction.user.id
|
||||
});
|
||||
return;
|
||||
throw error;
|
||||
} finally {
|
||||
await recordCommandMetric(context.redis, {
|
||||
ok,
|
||||
durationMs: Date.now() - started
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
|
||||
const command = map.get(interaction.commandName);
|
||||
if (!command) {
|
||||
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);
|
||||
}
|
||||
|
||||
export async function routeContextMenu(
|
||||
interaction: MessageContextMenuCommandInteraction,
|
||||
context: BotContext
|
||||
) {
|
||||
const command = contextMenuMap.get(interaction.commandName);
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
|
||||
if (await isUserBlacklisted(context.prisma, interaction.user.id)) {
|
||||
await interaction.reply({
|
||||
content: t(locale, 'generic.userBlacklisted'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (interaction.guildId && (await isGuildBlacklisted(context.prisma, interaction.guildId))) {
|
||||
await interaction.reply({
|
||||
content: t(locale, 'generic.guildBlacklisted'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const command = contextMenuMap.get(interaction.commandName);
|
||||
if (!command) {
|
||||
await interaction.reply({ content: t(locale, 'generic.unknownCommand'), ephemeral: true });
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user