Fix slash command timeouts: defer before locale DB reads
- interactionCreate: deferReply(ephemeral) immediately for chat commands, then getInteractionLocale. - All slash handlers use editReply; remove redundant deferReply in audit/fishing/music. - localeHelper: parallelize user/guild Prisma lookups for getInteractionLocale and getContextLocale. Note: deferred replies are ephemeral; public-only responses (e.g. music queue) are now user-visible only. Made-with: Cursor
This commit is contained in:
parent
a2e755b708
commit
1a4652c185
|
|
@ -118,9 +118,8 @@ export default {
|
||||||
|
|
||||||
if (action === 'set') {
|
if (action === 'set') {
|
||||||
const channel = interaction.options.getChannel('channel') as TextChannel;
|
const channel = interaction.options.getChannel('channel') as TextChannel;
|
||||||
if (!channel) return interaction.reply({ content: '❌ `channel` 옵션을 선택해주세요.', ephemeral: true });
|
if (!channel) return interaction.editReply({ content: '❌ `channel` 옵션을 선택해주세요.' });
|
||||||
|
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
const botMember = guild.members.me;
|
const botMember = guild.members.me;
|
||||||
if (!botMember) return;
|
if (!botMember) return;
|
||||||
const perms = channel.permissionsFor(botMember);
|
const perms = channel.permissionsFor(botMember);
|
||||||
|
|
@ -141,13 +140,11 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'clear') {
|
if (action === 'clear') {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
await auditLogService.clearChannel(guild.id);
|
await auditLogService.clearChannel(guild.id);
|
||||||
return interaction.editReply({ content: `✅ 감사 채널 설정이 해제되었습니다.` });
|
return interaction.editReply({ content: `✅ 감사 채널 설정이 해제되었습니다.` });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'status') {
|
if (action === 'status') {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
const config = await auditLogService.getChannel(guild.id);
|
const config = await auditLogService.getChannel(guild.id);
|
||||||
if (!config) return interaction.editReply({ content: `설정된 감사 채널이 없습니다. 먼저 \`/audit channel action:set\` 명령어로 채널을 설정해주세요.` });
|
if (!config) return interaction.editReply({ content: `설정된 감사 채널이 없습니다. 먼저 \`/audit channel action:set\` 명령어로 채널을 설정해주세요.` });
|
||||||
|
|
||||||
|
|
@ -168,10 +165,9 @@ export default {
|
||||||
const enable = interaction.options.getBoolean('enable');
|
const enable = interaction.options.getBoolean('enable');
|
||||||
|
|
||||||
if (!category || enable === null) {
|
if (!category || enable === null) {
|
||||||
return interaction.reply({ content: '❌ `category` 및 `enable` 옵션을 모두 입력해주세요.', ephemeral: true });
|
return interaction.editReply({ content: '❌ `category` 및 `enable` 옵션을 모두 입력해주세요.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
const config = await auditLogService.getChannel(guild.id);
|
const config = await auditLogService.getChannel(guild.id);
|
||||||
if (!config) return interaction.editReply({ content: `설정된 감사 채널이 없습니다. 먼저 \`/audit channel action:set\` 명령어로 채널을 설정해주세요.` });
|
if (!config) return interaction.editReply({ content: `설정된 감사 채널이 없습니다. 먼저 \`/audit channel action:set\` 명령어로 채널을 설정해주세요.` });
|
||||||
|
|
||||||
|
|
@ -185,7 +181,6 @@ export default {
|
||||||
const action = interaction.options.getString('action', true);
|
const action = interaction.options.getString('action', true);
|
||||||
|
|
||||||
if (action === 'permissions') {
|
if (action === 'permissions') {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
const results = await PermissionAuditService.auditGuild(guild);
|
const results = await PermissionAuditService.auditGuild(guild);
|
||||||
if (results.length === 0) return interaction.editReply({ content: t(locale, 'commands.permissionAudit.noResults') });
|
if (results.length === 0) return interaction.editReply({ content: t(locale, 'commands.permissionAudit.noResults') });
|
||||||
|
|
||||||
|
|
@ -230,8 +225,7 @@ export default {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in audit command', error);
|
console.error('Error in audit command', error);
|
||||||
const reply = interaction.deferred ? interaction.editReply : interaction.reply;
|
return interaction.editReply({ content: '명령 실행 중 오류가 발생했습니다.' });
|
||||||
return (reply as any)({ content: '명령 실행 중 오류가 발생했습니다.', ephemeral: true });
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,13 @@ export default {
|
||||||
if (action === 'language') {
|
if (action === 'language') {
|
||||||
const newLocale = interaction.options.getString('locale') as SupportedLocale;
|
const newLocale = interaction.options.getString('locale') as SupportedLocale;
|
||||||
if (!newLocale) {
|
if (!newLocale) {
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: '❌ `locale` 옵션을 선택해주세요.',
|
content: '❌ `locale` 옵션을 선택해주세요.',
|
||||||
ephemeral: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SUPPORTED_LOCALES.includes(newLocale)) {
|
if (!SUPPORTED_LOCALES.includes(newLocale)) {
|
||||||
return interaction.reply({ content: `Unsupported locale: ${newLocale}`, ephemeral: true });
|
return interaction.editReply({ content: `Unsupported locale: ${newLocale}` });
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.guildConfig.upsert({
|
await prisma.guildConfig.upsert({
|
||||||
|
|
@ -92,9 +91,8 @@ export default {
|
||||||
create: { guildId: interaction.guildId, locale: newLocale },
|
create: { guildId: interaction.guildId, locale: newLocale },
|
||||||
});
|
});
|
||||||
|
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: t(newLocale, 'commands.language.serverSet', { locale: newLocale === 'ko' ? '한국어' : 'English' }),
|
content: t(newLocale, 'commands.language.serverSet', { locale: newLocale === 'ko' ? '한국어' : 'English' }),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +130,7 @@ export default {
|
||||||
.setTitle(t(locale, 'commands.config.title'))
|
.setTitle(t(locale, 'commands.config.title'))
|
||||||
.setDescription(`${label}: **${state}**`);
|
.setDescription(`${label}: **${state}**`);
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -201,26 +201,23 @@ export default {
|
||||||
|
|
||||||
const startsAt = parseSeoulDateTime(date, time);
|
const startsAt = parseSeoulDateTime(date, time);
|
||||||
if (!startsAt) {
|
if (!startsAt) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: `${t(locale, 'commands.event.invalidDateTime')}\n${t(locale, 'commands.event.invalidDateTimeResolution')}`,
|
content: `${t(locale, 'commands.event.invalidDateTime')}\n${t(locale, 'commands.event.invalidDateTimeResolution')}`,
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startsAt.getTime() <= Date.now()) {
|
if (startsAt.getTime() <= Date.now()) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: `${t(locale, 'commands.event.invalidPastDateTime')}\n${t(locale, 'commands.event.invalidPastDateTimeResolution')}`,
|
content: `${t(locale, 'commands.event.invalidPastDateTime')}\n${t(locale, 'commands.event.invalidPastDateTimeResolution')}`,
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reminderOffsets = parseReminderOffsets(reminderRaw);
|
const reminderOffsets = parseReminderOffsets(reminderRaw);
|
||||||
if (!reminderOffsets) {
|
if (!reminderOffsets) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: `${t(locale, 'commands.event.invalidReminderOffsets')}\n${t(locale, 'commands.event.invalidReminderOffsetsResolution')}`,
|
content: `${t(locale, 'commands.event.invalidReminderOffsets')}\n${t(locale, 'commands.event.invalidReminderOffsetsResolution')}`,
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +249,7 @@ export default {
|
||||||
)
|
)
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
await interaction.editReply({ embeds: [embed] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,9 +265,8 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (events.length === 0) {
|
if (events.length === 0) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.listEmpty'),
|
content: t(locale, 'commands.event.listEmpty'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +291,7 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
await interaction.editReply({ embeds: [embed] });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -311,9 +307,8 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.cancelNotFound', { id }),
|
content: t(locale, 'commands.event.cancelNotFound', { id }),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -323,9 +318,8 @@ export default {
|
||||||
data: { status: 'CANCELLED' },
|
data: { status: 'CANCELLED' },
|
||||||
});
|
});
|
||||||
|
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.cancelSuccess', { id }),
|
content: t(locale, 'commands.event.cancelSuccess', { id }),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -340,34 +334,30 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.cancelNotFound', { id }),
|
content: t(locale, 'commands.event.cancelNotFound', { id }),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.announcementChannelId) {
|
if (!event.announcementChannelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.announceNotAvailable'),
|
content: t(locale, 'commands.event.announceNotAvailable'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await EventService.announceEvent(interaction.guild!, event.id);
|
await EventService.announceEvent(interaction.guild!, event.id);
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.announceSuccess', {
|
content: t(locale, 'commands.event.announceSuccess', {
|
||||||
id,
|
id,
|
||||||
channel: `<#${event.announcementChannelId}>`,
|
channel: `<#${event.announcementChannelId}>`,
|
||||||
}),
|
}),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.event.announceNotAvailable'),
|
content: t(locale, 'commands.event.announceNotAvailable'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,6 @@ export default {
|
||||||
const subcommand = interaction.options.getSubcommand();
|
const subcommand = interaction.options.getSubcommand();
|
||||||
|
|
||||||
if (subcommand === 'enter') {
|
if (subcommand === 'enter') {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
|
|
||||||
if (!config || !config.enabled) {
|
if (!config || !config.enabled) {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.fishing.disabled'),
|
content: t(locale, 'commands.fishing.disabled'),
|
||||||
|
|
@ -83,8 +81,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'cast') {
|
if (subcommand === 'cast') {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
|
|
||||||
if (!config || !config.enabled) {
|
if (!config || !config.enabled) {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.fishing.disabled'),
|
content: t(locale, 'commands.fishing.disabled'),
|
||||||
|
|
@ -109,8 +105,6 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'end') {
|
if (subcommand === 'end') {
|
||||||
await interaction.deferReply({ ephemeral: true });
|
|
||||||
|
|
||||||
const ended = await FishingService.endThreadByUser(interaction, locale);
|
const ended = await FishingService.endThreadByUser(interaction, locale);
|
||||||
if (!ended) {
|
if (!ended) {
|
||||||
await interaction.editReply({
|
await interaction.editReply({
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default {
|
||||||
|
|
||||||
// Validate locale (safety check)
|
// Validate locale (safety check)
|
||||||
if (!SUPPORTED_LOCALES.includes(newLocale)) {
|
if (!SUPPORTED_LOCALES.includes(newLocale)) {
|
||||||
await interaction.reply({ content: `Unsupported locale: ${newLocale}`, ephemeral: true });
|
await interaction.editReply({ content: `Unsupported locale: ${newLocale}` });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,9 +38,8 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Respond in the NEWLY selected locale
|
// Respond in the NEWLY selected locale
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(newLocale, 'commands.language.userSet', { locale: newLocale === 'ko' ? '한국어' : 'English' }),
|
content: t(newLocale, 'commands.language.userSet', { locale: newLocale === 'ko' ? '한국어' : 'English' }),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export default {
|
||||||
.setTitle('🎮 미니게임 설정 변경')
|
.setTitle('🎮 미니게임 설정 변경')
|
||||||
.setDescription(`${game.name} 미니게임이 **${state}**되었습니다.`);
|
.setDescription(`${game.name} 미니게임이 **${state}**되었습니다.`);
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'status') {
|
if (subcommand === 'status') {
|
||||||
|
|
@ -106,7 +106,7 @@ export default {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'channel') {
|
if (subcommand === 'channel') {
|
||||||
|
|
@ -127,7 +127,7 @@ export default {
|
||||||
.setTitle('🎮 미니게임 채널 설정')
|
.setTitle('🎮 미니게임 채널 설정')
|
||||||
.setDescription(`${game.name} 미니게임 전용 채널이 **${channelMsg}**로 설정되었습니다.`);
|
.setDescription(`${game.name} 미니게임 전용 채널이 **${channelMsg}**로 설정되었습니다.`);
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ async function respond(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ content, ephemeral });
|
await interaction.editReply({ content });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -136,33 +136,28 @@ export default {
|
||||||
const url = interaction.options.getString('url');
|
const url = interaction.options.getString('url');
|
||||||
|
|
||||||
if ((!query && !url) || (query && url)) {
|
if ((!query && !url) || (query && url)) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'addMutuallyExclusive'),
|
content: buildErrorMessage(locale, 'addMutuallyExclusive'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const textChannel = interaction.channel as any;
|
const textChannel = interaction.channel as any;
|
||||||
if (!textChannel?.send) {
|
if (!textChannel?.send) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'errors.E3003.userMessage'),
|
content: t(locale, 'errors.E3003.userMessage'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.deferReply();
|
|
||||||
|
|
||||||
const result = query
|
const result = query
|
||||||
? await MusicService.addFromQuery(member, textChannel, query, locale)
|
? await MusicService.addFromQuery(member, textChannel, query, locale)
|
||||||
: await MusicService.addFromUrl(member, textChannel, url!, locale);
|
: await MusicService.addFromUrl(member, textChannel, url!, locale);
|
||||||
|
|
@ -191,7 +186,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'queue') {
|
if (subcommand === 'queue') {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
embeds: [MusicService.getQueueEmbed(interaction.guildId!, locale)],
|
embeds: [MusicService.getQueueEmbed(interaction.guildId!, locale)],
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
@ -200,18 +195,16 @@ export default {
|
||||||
if (subcommand === 'remove') {
|
if (subcommand === 'remove') {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
||||||
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -219,14 +212,13 @@ export default {
|
||||||
const index = interaction.options.getInteger('index', true);
|
const index = interaction.options.getInteger('index', true);
|
||||||
const removed = await MusicService.remove(interaction.guildId!, index);
|
const removed = await MusicService.remove(interaction.guildId!, index);
|
||||||
if (!removed) {
|
if (!removed) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'noActiveSession'),
|
content: buildErrorMessage(locale, 'noActiveSession'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: t(locale, 'commands.music.queueRemoved', {
|
content: t(locale, 'commands.music.queueRemoved', {
|
||||||
title: removed.title,
|
title: removed.title,
|
||||||
}),
|
}),
|
||||||
|
|
@ -237,160 +229,145 @@ export default {
|
||||||
if (subcommand === 'pause') {
|
if (subcommand === 'pause') {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
||||||
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const paused = await MusicService.pause(interaction.guildId!, locale);
|
const paused = await MusicService.pause(interaction.guildId!, locale);
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'noActiveSession'),
|
content: buildErrorMessage(locale, 'noActiveSession'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ content: t(locale, 'commands.music.pauseSuccess') });
|
await interaction.editReply({ content: t(locale, 'commands.music.pauseSuccess') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'resume') {
|
if (subcommand === 'resume') {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
||||||
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const resumed = await MusicService.resume(interaction.guildId!, locale);
|
const resumed = await MusicService.resume(interaction.guildId!, locale);
|
||||||
if (!resumed) {
|
if (!resumed) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'noActiveSession'),
|
content: buildErrorMessage(locale, 'noActiveSession'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ content: t(locale, 'commands.music.resumeSuccess') });
|
await interaction.editReply({ content: t(locale, 'commands.music.resumeSuccess') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'skip') {
|
if (subcommand === 'skip') {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
||||||
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const skipped = await MusicService.skip(interaction.guildId!);
|
const skipped = await MusicService.skip(interaction.guildId!);
|
||||||
if (!skipped) {
|
if (!skipped) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'noActiveSession'),
|
content: buildErrorMessage(locale, 'noActiveSession'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ content: t(locale, 'commands.music.skipSuccess') });
|
await interaction.editReply({ content: t(locale, 'commands.music.skipSuccess') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'stop') {
|
if (subcommand === 'stop') {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
||||||
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stopped = await MusicService.stop(interaction.guildId!, locale);
|
const stopped = await MusicService.stop(interaction.guildId!, locale);
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'noActiveSession'),
|
content: buildErrorMessage(locale, 'noActiveSession'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ content: t(locale, 'commands.music.stopSuccess') });
|
await interaction.editReply({ content: t(locale, 'commands.music.stopSuccess') });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subcommand === 'leave') {
|
if (subcommand === 'leave') {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member.voice.channel) {
|
if (!member.voice.channel) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'notInVoice'),
|
content: buildErrorMessage(locale, 'notInVoice'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
const activeVoiceChannelId = MusicService.getActiveVoiceChannelId(interaction.guildId!);
|
||||||
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
if (activeVoiceChannelId && activeVoiceChannelId !== member.voice.channelId) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
content: buildErrorMessage(locale, 'differentVoiceChannel'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const left = await MusicService.leave(interaction.guildId!, locale);
|
const left = await MusicService.leave(interaction.guildId!, locale);
|
||||||
if (!left) {
|
if (!left) {
|
||||||
await interaction.reply({
|
await interaction.editReply({
|
||||||
content: buildErrorMessage(locale, 'noActiveSession'),
|
content: buildErrorMessage(locale, 'noActiveSession'),
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.reply({ content: t(locale, 'commands.music.leaveSuccess') });
|
await interaction.editReply({ content: t(locale, 'commands.music.leaveSuccess') });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in music command:', error);
|
logger.error('Error in music command:', error);
|
||||||
|
|
@ -417,15 +394,7 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interaction.replied || interaction.deferred) {
|
await respond(interaction, t(locale, 'errors.E3003.userMessage'), true);
|
||||||
await respond(interaction, t(locale, 'errors.E3003.userMessage'), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.reply({
|
|
||||||
content: t(locale, 'errors.E3003.userMessage'),
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -97,11 +97,11 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!config || !config.enabled) {
|
if (!config || !config.enabled) {
|
||||||
return interaction.reply({ content: '❌ 이 서버에서는 재련 미니게임이 비활성화되어 있습니다.', ephemeral: true });
|
return interaction.editReply({ content: '❌ 이 서버에서는 재련 미니게임이 비활성화되어 있습니다.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.channelId && config.channelId !== interaction.channelId) {
|
if (config.channelId && config.channelId !== interaction.channelId) {
|
||||||
return interaction.reply({ content: `❌ 재련 미니게임은 <#${config.channelId}> 채널에서만 이용 가능합니다.`, ephemeral: true });
|
return interaction.editReply({ content: `❌ 재련 미니게임은 <#${config.channelId}> 채널에서만 이용 가능합니다.` });
|
||||||
}
|
}
|
||||||
|
|
||||||
const subcommand = interaction.options.getSubcommand();
|
const subcommand = interaction.options.getSubcommand();
|
||||||
|
|
@ -132,9 +132,9 @@ export default {
|
||||||
|
|
||||||
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(retryBtn);
|
const row = new ActionRowBuilder<ButtonBuilder>().addComponents(retryBtn);
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed], components: [row] });
|
return interaction.editReply({ embeds: [embed], components: [row] });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return interaction.reply({ content: `❌ 오류: ${err.message}`, ephemeral: true });
|
return interaction.editReply({ content: `❌ 오류: ${err.message}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,10 +142,10 @@ export default {
|
||||||
if (subcommand === 'battle') {
|
if (subcommand === 'battle') {
|
||||||
const targetUser = interaction.options.getUser('target', true);
|
const targetUser = interaction.options.getUser('target', true);
|
||||||
if (targetUser.id === interaction.user.id) {
|
if (targetUser.id === interaction.user.id) {
|
||||||
return interaction.reply({ content: '❌ 자신을 공격할 수 없습니다.', ephemeral: true });
|
return interaction.editReply({ content: '❌ 자신을 공격할 수 없습니다.' });
|
||||||
}
|
}
|
||||||
if (targetUser.bot) {
|
if (targetUser.bot) {
|
||||||
return interaction.reply({ content: '❌ 봇과 전투할 수 없습니다.', ephemeral: true });
|
return interaction.editReply({ content: '❌ 봇과 전투할 수 없습니다.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -170,9 +170,9 @@ export default {
|
||||||
if (result.attackerDurability <= 0) embed.setFooter({ text: '⚠️ [경고] 무기가 전투 불능이 되었습니다. 내구도 0에서 다시 공격하면 파괴됩니다!' });
|
if (result.attackerDurability <= 0) embed.setFooter({ text: '⚠️ [경고] 무기가 전투 불능이 되었습니다. 내구도 0에서 다시 공격하면 파괴됩니다!' });
|
||||||
}
|
}
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed] });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return interaction.reply({ content: `❌ 오류: ${err.message}`, ephemeral: true });
|
return interaction.editReply({ content: `❌ 오류: ${err.message}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,9 +180,9 @@ export default {
|
||||||
if (subcommand === 'checkin') {
|
if (subcommand === 'checkin') {
|
||||||
try {
|
try {
|
||||||
const res = await RefinementService.checkIn(interaction.user.id, interaction.guildId);
|
const res = await RefinementService.checkIn(interaction.user.id, interaction.guildId);
|
||||||
return interaction.reply({ content: `✅ 출석 완료! **${res.goldAdded} G**를 수령했습니다. (총: ${res.totalGold} G)`, ephemeral: true });
|
return interaction.editReply({ content: `✅ 출석 완료! **${res.goldAdded} G**를 수령했습니다. (총: ${res.totalGold} G)` });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return interaction.reply({ content: `❌ 오류: ${err.message}`, ephemeral: true });
|
return interaction.editReply({ content: `❌ 오류: ${err.message}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,7 +208,7 @@ export default {
|
||||||
{ name: '전투', value: `승리: ${profile.battleWin} | 패배: ${profile.battleLoss}`, inline: false }
|
{ name: '전투', value: `승리: ${profile.battleWin} | 패배: ${profile.battleLoss}`, inline: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed] });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- RANKING ---
|
// --- RANKING ---
|
||||||
|
|
@ -243,16 +243,16 @@ export default {
|
||||||
}).join('\n') || '데이터가 없습니다.';
|
}).join('\n') || '데이터가 없습니다.';
|
||||||
|
|
||||||
embed.setDescription(listStr);
|
embed.setDescription(listStr);
|
||||||
return interaction.reply({ embeds: [embed] });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SELL ---
|
// --- SELL ---
|
||||||
if (subcommand === 'sell') {
|
if (subcommand === 'sell') {
|
||||||
try {
|
try {
|
||||||
const res = await RefinementService.sellWeapon(interaction.user.id, interaction.guildId);
|
const res = await RefinementService.sellWeapon(interaction.user.id, interaction.guildId);
|
||||||
return interaction.reply({ content: `💰 **${res.level}단계** 무기를 판매하고 **${res.price} G**를 받았습니다. (총: ${res.gold} G)`, ephemeral: true });
|
return interaction.editReply({ content: `💰 **${res.level}단계** 무기를 판매하고 **${res.price} G**를 받았습니다. (총: ${res.gold} G)` });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return interaction.reply({ content: `❌ 오류: ${err.message}`, ephemeral: true });
|
return interaction.editReply({ content: `❌ 오류: ${err.message}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +271,7 @@ export default {
|
||||||
{ name: '🔥 피버 타임', value: '서버가 가장 활발한 시간대에 재련 성공 확률이 10% 증가합니다!' }
|
{ name: '🔥 피버 타임', value: '서버가 가장 활발한 시간대에 재련 성공 확률이 10% 증가합니다!' }
|
||||||
);
|
);
|
||||||
|
|
||||||
return interaction.reply({ embeds: [embed] });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,9 @@ export default {
|
||||||
if (!interaction.guildId) return;
|
if (!interaction.guildId) return;
|
||||||
|
|
||||||
const { embed, components } = await SetupWizardRenderer.renderStep(0, interaction, locale);
|
const { embed, components } = await SetupWizardRenderer.renderStep(0, interaction, locale);
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
embeds: [embed],
|
embeds: [embed],
|
||||||
components,
|
components,
|
||||||
ephemeral: true,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -88,22 +88,21 @@ export default {
|
||||||
|
|
||||||
if (action === 'set') {
|
if (action === 'set') {
|
||||||
const channel = interaction.options.getChannel('channel');
|
const channel = interaction.options.getChannel('channel');
|
||||||
if (!channel) return interaction.reply({ content: '❌ `channel` 옵션을 선택해주세요.', ephemeral: true });
|
if (!channel) return interaction.editReply({ content: '❌ `channel` 옵션을 선택해주세요.' });
|
||||||
|
|
||||||
await prisma.voiceGenerator.upsert({
|
await prisma.voiceGenerator.upsert({
|
||||||
where: { channelId: channel.id },
|
where: { channelId: channel.id },
|
||||||
update: { categoryId: category?.id || null, guildId },
|
update: { categoryId: category?.id || null, guildId },
|
||||||
create: { channelId: channel.id, guildId, categoryId: category?.id || null }
|
create: { channelId: channel.id, guildId, categoryId: category?.id || null }
|
||||||
});
|
});
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: t(locale, 'commands.voiceSetup.setSuccess', { channel: `${channel}` }),
|
content: t(locale, 'commands.voiceSetup.setSuccess', { channel: `${channel}` }),
|
||||||
ephemeral: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
const name = interaction.options.getString('name');
|
const name = interaction.options.getString('name');
|
||||||
if (!name) return interaction.reply({ content: '❌ `name` 옵션을 입력해주세요.', ephemeral: true });
|
if (!name) return interaction.editReply({ content: '❌ `name` 옵션을 입력해주세요.' });
|
||||||
|
|
||||||
const newChannel = await interaction.guild!.channels.create({
|
const newChannel = await interaction.guild!.channels.create({
|
||||||
name,
|
name,
|
||||||
|
|
@ -113,9 +112,8 @@ export default {
|
||||||
await prisma.voiceGenerator.create({
|
await prisma.voiceGenerator.create({
|
||||||
data: { channelId: newChannel.id, guildId, categoryId: category?.id || null }
|
data: { channelId: newChannel.id, guildId, categoryId: category?.id || null }
|
||||||
});
|
});
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: t(locale, 'commands.voiceSetup.createSuccess', { channel: `${newChannel}` }),
|
content: t(locale, 'commands.voiceSetup.createSuccess', { channel: `${newChannel}` }),
|
||||||
ephemeral: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,31 +124,29 @@ export default {
|
||||||
|
|
||||||
if (action === 'name') {
|
if (action === 'name') {
|
||||||
const template = interaction.options.getString('template');
|
const template = interaction.options.getString('template');
|
||||||
if (!template) return interaction.reply({ content: '❌ `template` 옵션을 입력해주세요.', ephemeral: true });
|
if (!template) return interaction.editReply({ content: '❌ `template` 옵션을 입력해주세요.' });
|
||||||
|
|
||||||
await prisma.voiceGuildConfig.upsert({
|
await prisma.voiceGuildConfig.upsert({
|
||||||
where: { guildId },
|
where: { guildId },
|
||||||
update: { defaultNameTemplate: template },
|
update: { defaultNameTemplate: template },
|
||||||
create: { guildId, defaultNameTemplate: template }
|
create: { guildId, defaultNameTemplate: template }
|
||||||
});
|
});
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: t(locale, 'commands.voiceConfig.setSuccess'),
|
content: t(locale, 'commands.voiceConfig.setSuccess'),
|
||||||
ephemeral: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action === 'limit') {
|
if (action === 'limit') {
|
||||||
const limit = interaction.options.getInteger('limit');
|
const limit = interaction.options.getInteger('limit');
|
||||||
if (limit === null) return interaction.reply({ content: '❌ `limit` 옵션을 입력해주세요.', ephemeral: true });
|
if (limit === null) return interaction.editReply({ content: '❌ `limit` 옵션을 입력해주세요.' });
|
||||||
|
|
||||||
await prisma.voiceGuildConfig.upsert({
|
await prisma.voiceGuildConfig.upsert({
|
||||||
where: { guildId },
|
where: { guildId },
|
||||||
update: { defaultUserLimit: limit },
|
update: { defaultUserLimit: limit },
|
||||||
create: { guildId, defaultUserLimit: limit }
|
create: { guildId, defaultUserLimit: limit }
|
||||||
});
|
});
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: t(locale, 'commands.voiceConfig.setSuccess'),
|
content: t(locale, 'commands.voiceConfig.setSuccess'),
|
||||||
ephemeral: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,14 +167,13 @@ export default {
|
||||||
inline: true
|
inline: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return interaction.reply({ embeds: [embed], ephemeral: true });
|
return interaction.editReply({ embeds: [embed] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error in voice command', error);
|
logger.error('Error in voice command', error);
|
||||||
return interaction.reply({
|
return interaction.editReply({
|
||||||
content: t(locale, 'errors.E3003.userMessage'),
|
content: t(locale, 'errors.E3003.userMessage'),
|
||||||
ephemeral: true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ export default {
|
||||||
const command = client.commands.get(interaction.commandName);
|
const command = client.commands.get(interaction.commandName);
|
||||||
if (!command) return;
|
if (!command) return;
|
||||||
|
|
||||||
|
// Acknowledge before locale DB reads so Discord's ~3s interaction window is never missed.
|
||||||
|
await interaction.deferReply({ ephemeral: true });
|
||||||
const locale = await getInteractionLocale(interaction);
|
const locale = await getInteractionLocale(interaction);
|
||||||
await withErrorHandler(interaction, async () => {
|
await withErrorHandler(interaction, async () => {
|
||||||
await command.execute(interaction, locale);
|
await command.execute(interaction, locale);
|
||||||
|
|
|
||||||
|
|
@ -23,21 +23,20 @@ export async function getInteractionLocale(interaction: Interaction): Promise<Su
|
||||||
let guildLocale: string | null = null;
|
let guildLocale: string | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fetch user locale preference
|
const [userPref, guildConfig] = await Promise.all([
|
||||||
const userPref = await prisma.userLocale.findUnique({
|
prisma.userLocale.findUnique({
|
||||||
where: { userId: interaction.user.id },
|
where: { userId: interaction.user.id },
|
||||||
select: { locale: true },
|
|
||||||
});
|
|
||||||
userLocale = userPref?.locale ?? null;
|
|
||||||
|
|
||||||
// Fetch guild locale preference
|
|
||||||
if (interaction.guildId) {
|
|
||||||
const guildConfig = await prisma.guildConfig.findUnique({
|
|
||||||
where: { guildId: interaction.guildId },
|
|
||||||
select: { locale: true },
|
select: { locale: true },
|
||||||
});
|
}),
|
||||||
guildLocale = guildConfig?.locale ?? null;
|
interaction.guildId
|
||||||
}
|
? prisma.guildConfig.findUnique({
|
||||||
|
where: { guildId: interaction.guildId },
|
||||||
|
select: { locale: true },
|
||||||
|
})
|
||||||
|
: Promise.resolve(null),
|
||||||
|
]);
|
||||||
|
userLocale = userPref?.locale ?? null;
|
||||||
|
guildLocale = guildConfig?.locale ?? null;
|
||||||
} catch {
|
} catch {
|
||||||
// If DB lookup fails, fall through to Discord locale / default
|
// If DB lookup fails, fall through to Discord locale / default
|
||||||
}
|
}
|
||||||
|
|
@ -61,21 +60,22 @@ export async function getContextLocale(
|
||||||
let guildLocale: string | null = null;
|
let guildLocale: string | null = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (userId) {
|
const [userPref, guildConfig] = await Promise.all([
|
||||||
const userPref = await prisma.userLocale.findUnique({
|
userId
|
||||||
where: { userId },
|
? prisma.userLocale.findUnique({
|
||||||
select: { locale: true },
|
where: { userId },
|
||||||
});
|
select: { locale: true },
|
||||||
userLocale = userPref?.locale ?? null;
|
})
|
||||||
}
|
: Promise.resolve(null),
|
||||||
|
guildId
|
||||||
if (guildId) {
|
? prisma.guildConfig.findUnique({
|
||||||
const guildConfig = await prisma.guildConfig.findUnique({
|
where: { guildId },
|
||||||
where: { guildId },
|
select: { locale: true },
|
||||||
select: { locale: true },
|
})
|
||||||
});
|
: Promise.resolve(null),
|
||||||
guildLocale = guildConfig?.locale ?? null;
|
]);
|
||||||
}
|
userLocale = userPref?.locale ?? null;
|
||||||
|
guildLocale = guildConfig?.locale ?? null;
|
||||||
} catch {
|
} catch {
|
||||||
// Fall through to default
|
// Fall through to default
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue