Enhance moderation commands with locale support and permission checks
- Updated moderation commands to utilize locale for user-facing messages, improving internationalization. - Refactored permission checks to include locale as a parameter, ensuring accurate responses based on the server's language settings. - Added new translation keys for bot permissions and command restrictions in both German and English.
This commit is contained in:
18
apps/bot/src/i18n.ts
Normal file
18
apps/bot/src/i18n.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { LocaleSchema, type Locale } from '@nexumi/shared';
|
||||
import type { PrismaClient } from '@prisma/client';
|
||||
import { env } from './env.js';
|
||||
|
||||
export async function getGuildLocale(
|
||||
prisma: PrismaClient,
|
||||
guildId: string | null
|
||||
): Promise<Locale> {
|
||||
if (!guildId) {
|
||||
return env.DEFAULT_LOCALE;
|
||||
}
|
||||
const settings = await prisma.guildSettings.findUnique({
|
||||
where: { guildId },
|
||||
select: { locale: true }
|
||||
});
|
||||
const parsed = LocaleSchema.safeParse(settings?.locale);
|
||||
return parsed.success ? parsed.data : env.DEFAULT_LOCALE;
|
||||
}
|
||||
@@ -6,22 +6,27 @@ import {
|
||||
type ChatInputCommandInteraction,
|
||||
type GuildMember
|
||||
} from 'discord.js';
|
||||
import { t } from '@nexumi/shared';
|
||||
import { t, tf } from '@nexumi/shared';
|
||||
import type { SlashCommand } from '../../types.js';
|
||||
import { requirePermission } from '../../permissions.js';
|
||||
import { applyWarnEscalation, createCase, scheduleTempBanExpire } from './service.js';
|
||||
import { parseDuration } from './duration.js';
|
||||
import { getGuildLocale } from '../../i18n.js';
|
||||
|
||||
function reasonFrom(i: ChatInputCommandInteraction): string {
|
||||
return i.options.getString('reason') ?? t('de', 'moderation.reason.none');
|
||||
function reasonFrom(i: ChatInputCommandInteraction, locale: 'de' | 'en'): string {
|
||||
return i.options.getString('reason') ?? t(locale, 'moderation.reason.none');
|
||||
}
|
||||
|
||||
async function ensureMod(interaction: ChatInputCommandInteraction, permission: bigint): Promise<boolean> {
|
||||
async function ensureMod(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
permission: bigint,
|
||||
locale: 'de' | 'en'
|
||||
): Promise<boolean> {
|
||||
if (!interaction.inGuild()) {
|
||||
await interaction.reply({ content: 'Guild only command.', ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
|
||||
return false;
|
||||
}
|
||||
return requirePermission(interaction, permission);
|
||||
return requirePermission(interaction, permission, locale);
|
||||
}
|
||||
|
||||
const banCommand: SlashCommand = {
|
||||
@@ -32,9 +37,10 @@ const banCommand: SlashCommand = {
|
||||
.addStringOption((opt) => opt.setName('reason').setDescription('Reason'))
|
||||
.addStringOption((opt) => opt.setName('duration').setDescription('Optional temp-ban duration (e.g. 7d)')),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.BanMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.BanMembers, locale))) return;
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const reason = reasonFrom(interaction);
|
||||
const reason = reasonFrom(interaction, locale);
|
||||
await interaction.guild!.members.ban(user.id, { reason });
|
||||
await createCase(context, {
|
||||
guildId: interaction.guildId!,
|
||||
@@ -47,7 +53,7 @@ const banCommand: SlashCommand = {
|
||||
if (duration) {
|
||||
await scheduleTempBanExpire(interaction.guildId!, user.id, parseDuration(duration), reason);
|
||||
}
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,9 +64,10 @@ const unbanCommand: SlashCommand = {
|
||||
.addStringOption((opt) => opt.setName('user_id').setDescription('Discord user id').setRequired(true))
|
||||
.addStringOption((opt) => opt.setName('reason').setDescription('Reason')),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.BanMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.BanMembers, locale))) return;
|
||||
const userId = interaction.options.getString('user_id', true);
|
||||
const reason = reasonFrom(interaction);
|
||||
const reason = reasonFrom(interaction, locale);
|
||||
await interaction.guild!.members.unban(userId, reason);
|
||||
await createCase(context, {
|
||||
guildId: interaction.guildId!,
|
||||
@@ -69,7 +76,7 @@ const unbanCommand: SlashCommand = {
|
||||
action: 'UNBAN',
|
||||
reason
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,9 +87,10 @@ const kickCommand: SlashCommand = {
|
||||
.addUserOption((opt) => opt.setName('user').setDescription('Target user').setRequired(true))
|
||||
.addStringOption((opt) => opt.setName('reason').setDescription('Reason')),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.KickMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.KickMembers, locale))) return;
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const reason = reasonFrom(interaction);
|
||||
const reason = reasonFrom(interaction, locale);
|
||||
const member = (await interaction.guild!.members.fetch(user.id)) as GuildMember;
|
||||
await member.kick(reason);
|
||||
await createCase(context, {
|
||||
@@ -92,7 +100,7 @@ const kickCommand: SlashCommand = {
|
||||
action: 'KICK',
|
||||
reason
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,10 +114,11 @@ const timeoutCommand: SlashCommand = {
|
||||
)
|
||||
.addStringOption((opt) => opt.setName('reason').setDescription('Reason')),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers, locale))) return;
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const duration = parseDuration(interaction.options.getString('duration', true));
|
||||
const reason = reasonFrom(interaction);
|
||||
const reason = reasonFrom(interaction, locale);
|
||||
const member = await interaction.guild!.members.fetch(user.id);
|
||||
await member.timeout(duration, reason);
|
||||
await createCase(context, {
|
||||
@@ -120,7 +129,7 @@ const timeoutCommand: SlashCommand = {
|
||||
reason,
|
||||
metadata: { durationMs: duration }
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,9 +140,10 @@ const untimeoutCommand: SlashCommand = {
|
||||
.addUserOption((opt) => opt.setName('user').setDescription('Target user').setRequired(true))
|
||||
.addStringOption((opt) => opt.setName('reason').setDescription('Reason')),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers, locale))) return;
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const reason = reasonFrom(interaction);
|
||||
const reason = reasonFrom(interaction, locale);
|
||||
const member = await interaction.guild!.members.fetch(user.id);
|
||||
await member.timeout(null, reason);
|
||||
await createCase(context, {
|
||||
@@ -143,7 +153,7 @@ const untimeoutCommand: SlashCommand = {
|
||||
action: 'UN_TIMEOUT',
|
||||
reason
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -203,7 +213,8 @@ const warnCommand: SlashCommand = {
|
||||
.addIntegerOption((o) => o.setName('count').setDescription('Warning count threshold').setRequired(true).setMinValue(1).setMaxValue(50))
|
||||
),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers, locale))) return;
|
||||
const sub = interaction.options.getSubcommand();
|
||||
if (sub === 'escalation-set') {
|
||||
const warnCount = interaction.options.getInteger('count', true);
|
||||
@@ -212,7 +223,10 @@ const warnCommand: SlashCommand = {
|
||||
const reason = interaction.options.getString('reason');
|
||||
const durationMs = durationInput ? parseDuration(durationInput) : null;
|
||||
if (action === 'TIMEOUT' && !durationMs) {
|
||||
await interaction.reply({ content: 'Duration is required for TIMEOUT escalation.', ephemeral: true });
|
||||
await interaction.reply({
|
||||
content: t(locale, 'moderation.escalation.durationRequired'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
await context.prisma.escalationRule.upsert({
|
||||
@@ -237,8 +251,13 @@ const warnCommand: SlashCommand = {
|
||||
enabled: true
|
||||
}
|
||||
});
|
||||
const durationText = durationMs ? ` (${durationMs} ms)` : '';
|
||||
await interaction.reply({
|
||||
content: `Escalation rule saved: ${warnCount} warnings => ${action}${durationMs ? ` (${durationMs} ms)` : ''}.`,
|
||||
content: tf(locale, 'moderation.escalation.saved', {
|
||||
warnCount,
|
||||
action,
|
||||
duration: durationText
|
||||
}),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
@@ -251,7 +270,7 @@ const warnCommand: SlashCommand = {
|
||||
});
|
||||
const content =
|
||||
rules.length === 0
|
||||
? 'No escalation rules configured.'
|
||||
? t(locale, 'moderation.escalation.noneConfigured')
|
||||
: rules
|
||||
.map(
|
||||
(rule) =>
|
||||
@@ -267,7 +286,10 @@ const warnCommand: SlashCommand = {
|
||||
await context.prisma.escalationRule.delete({
|
||||
where: { guildId_warnCount: { guildId: interaction.guildId!, warnCount } }
|
||||
});
|
||||
await interaction.reply({ content: `Escalation rule removed for ${warnCount} warnings.`, ephemeral: true });
|
||||
await interaction.reply({
|
||||
content: tf(locale, 'moderation.escalation.removed', { warnCount }),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -303,8 +325,8 @@ const warnCommand: SlashCommand = {
|
||||
);
|
||||
await interaction.reply({
|
||||
content: escalationResult
|
||||
? `Warning created: ${warning.id}\n${escalationResult}`
|
||||
: `Warning created: ${warning.id}`,
|
||||
? `${tf(locale, 'moderation.warning.created', { warningId: warning.id })}\n${escalationResult}`
|
||||
: tf(locale, 'moderation.warning.created', { warningId: warning.id }),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
@@ -319,8 +341,13 @@ const warnCommand: SlashCommand = {
|
||||
});
|
||||
const content =
|
||||
warnings.length === 0
|
||||
? 'No warnings.'
|
||||
: warnings.map((w: { id: string; reason: string | null }) => `- ${w.id}: ${w.reason ?? 'No reason'}`).join('\n');
|
||||
? t(locale, 'moderation.warning.none')
|
||||
: warnings
|
||||
.map(
|
||||
(w: { id: string; reason: string | null }) =>
|
||||
`- ${w.id}: ${w.reason ?? t(locale, 'moderation.warning.defaultReason')}`
|
||||
)
|
||||
.join('\n');
|
||||
await interaction.reply({ content, ephemeral: true });
|
||||
return;
|
||||
}
|
||||
@@ -335,7 +362,7 @@ const warnCommand: SlashCommand = {
|
||||
action: 'WARN_REMOVE',
|
||||
reason: `Warning ${warningId} removed`
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -348,7 +375,7 @@ const warnCommand: SlashCommand = {
|
||||
action: 'WARN_CLEAR',
|
||||
reason: 'Warnings cleared'
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -369,7 +396,8 @@ const purgeCommand: SlashCommand = {
|
||||
o.setName('regex').setDescription('Only purge messages matching regex')
|
||||
),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageMessages))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageMessages, locale))) return;
|
||||
const amount = interaction.options.getInteger('amount', true);
|
||||
const targetUser = interaction.options.getUser('user');
|
||||
const botsOnly = interaction.options.getBoolean('bots') ?? false;
|
||||
@@ -381,7 +409,7 @@ const purgeCommand: SlashCommand = {
|
||||
try {
|
||||
regex = new RegExp(regexInput, 'i');
|
||||
} catch {
|
||||
await interaction.reply({ content: 'Invalid regex pattern.', ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'generic.invalidRegex'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -408,7 +436,7 @@ const purgeCommand: SlashCommand = {
|
||||
action: 'PURGE',
|
||||
reason: `Deleted ${deletedCount} messages`
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -418,7 +446,8 @@ const slowmodeCommand: SlashCommand = {
|
||||
.setDescription('Set channel slowmode')
|
||||
.addIntegerOption((o) => o.setName('seconds').setDescription('0-21600').setRequired(true).setMinValue(0).setMaxValue(21600)),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageChannels))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageChannels, locale))) return;
|
||||
const seconds = interaction.options.getInteger('seconds', true);
|
||||
if (interaction.channel instanceof TextChannel) {
|
||||
await interaction.channel.setRateLimitPerUser(seconds);
|
||||
@@ -430,14 +459,15 @@ const slowmodeCommand: SlashCommand = {
|
||||
action: 'SLOWMODE',
|
||||
reason: `Set slowmode to ${seconds}s`
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
const lockCommand: SlashCommand = {
|
||||
data: new SlashCommandBuilder().setName('lock').setDescription('Lock current text channel'),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageChannels))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageChannels, locale))) return;
|
||||
if (interaction.channel?.type === ChannelType.GuildText) {
|
||||
await interaction.channel.permissionOverwrites.edit(interaction.guild!.roles.everyone, {
|
||||
SendMessages: false
|
||||
@@ -450,14 +480,15 @@ const lockCommand: SlashCommand = {
|
||||
action: 'LOCK',
|
||||
reason: 'Channel locked'
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
const unlockCommand: SlashCommand = {
|
||||
data: new SlashCommandBuilder().setName('unlock').setDescription('Unlock current text channel'),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageChannels))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageChannels, locale))) return;
|
||||
if (interaction.channel?.type === ChannelType.GuildText) {
|
||||
await interaction.channel.permissionOverwrites.edit(interaction.guild!.roles.everyone, {
|
||||
SendMessages: null
|
||||
@@ -470,7 +501,7 @@ const unlockCommand: SlashCommand = {
|
||||
action: 'UNLOCK',
|
||||
reason: 'Channel unlocked'
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -492,7 +523,8 @@ const nickCommand: SlashCommand = {
|
||||
.addUserOption((o) => o.setName('user').setDescription('Target').setRequired(true))
|
||||
),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageNicknames))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageNicknames, locale))) return;
|
||||
const sub = interaction.options.getSubcommand();
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const member = await interaction.guild!.members.fetch(user.id);
|
||||
@@ -516,7 +548,7 @@ const nickCommand: SlashCommand = {
|
||||
reason: 'Nickname reset'
|
||||
});
|
||||
}
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -538,14 +570,18 @@ const caseCommand: SlashCommand = {
|
||||
s.setName('delete').setDescription('Soft-delete case').addIntegerOption((o) => o.setName('id').setDescription('Case number').setRequired(true))
|
||||
),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers, locale))) return;
|
||||
const sub = interaction.options.getSubcommand();
|
||||
const caseNumber = interaction.options.getInteger('id', true);
|
||||
if (sub === 'view') {
|
||||
const found = await context.prisma.case.findUnique({
|
||||
where: { guildId_caseNumber: { guildId: interaction.guildId!, caseNumber } }
|
||||
});
|
||||
await interaction.reply({ content: found ? JSON.stringify(found, null, 2) : 'Case not found.', ephemeral: true });
|
||||
await interaction.reply({
|
||||
content: found ? JSON.stringify(found, null, 2) : t(locale, 'moderation.case.notFound'),
|
||||
ephemeral: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (sub === 'edit') {
|
||||
@@ -554,14 +590,14 @@ const caseCommand: SlashCommand = {
|
||||
where: { guildId_caseNumber: { guildId: interaction.guildId!, caseNumber } },
|
||||
data: { reason }
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await context.prisma.case.update({
|
||||
where: { guildId_caseNumber: { guildId: interaction.guildId!, caseNumber } },
|
||||
data: { deletedAt: new Date() }
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -583,7 +619,8 @@ const modNoteCommand: SlashCommand = {
|
||||
.addUserOption((o) => o.setName('user').setDescription('Target').setRequired(true))
|
||||
),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers))) return;
|
||||
const locale = await getGuildLocale(context.prisma, interaction.guildId);
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ModerateMembers, locale))) return;
|
||||
const sub = interaction.options.getSubcommand();
|
||||
const user = interaction.options.getUser('user', true);
|
||||
await context.prisma.user.upsert({ where: { id: user.id }, update: {}, create: { id: user.id } });
|
||||
@@ -604,7 +641,7 @@ const modNoteCommand: SlashCommand = {
|
||||
action: 'MODNOTE_ADD',
|
||||
reason: note
|
||||
});
|
||||
await interaction.reply({ content: t('de', 'moderation.success'), ephemeral: true });
|
||||
await interaction.reply({ content: t(locale, 'moderation.success'), ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const notes = await context.prisma.modNote.findMany({
|
||||
@@ -614,7 +651,7 @@ const modNoteCommand: SlashCommand = {
|
||||
});
|
||||
const content =
|
||||
notes.length === 0
|
||||
? 'No notes.'
|
||||
? t(locale, 'moderation.note.none')
|
||||
: notes.map((n: { note: string }) => `- ${n.note}`).join('\n');
|
||||
await interaction.reply({ content, ephemeral: true });
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { type ChatInputCommandInteraction } from 'discord.js';
|
||||
import { t } from '@nexumi/shared';
|
||||
import type { Locale } from '@nexumi/shared';
|
||||
|
||||
export async function requirePermission(
|
||||
interaction: ChatInputCommandInteraction,
|
||||
permission: bigint
|
||||
permission: bigint,
|
||||
locale: Locale
|
||||
): Promise<boolean> {
|
||||
const locale = 'de';
|
||||
const member = interaction.memberPermissions;
|
||||
if (!member?.has(permission)) {
|
||||
await interaction.reply({
|
||||
@@ -17,7 +18,10 @@ export async function requirePermission(
|
||||
|
||||
if (!interaction.guild?.members.me?.permissions.has(permission)) {
|
||||
await interaction.reply({
|
||||
content: `Bot lacks required permission (${permission.toString()}).`,
|
||||
content: t(locale, 'generic.botMissingPermission').replace(
|
||||
'{permission}',
|
||||
permission.toString()
|
||||
),
|
||||
ephemeral: true
|
||||
});
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user