187 lines
6.9 KiB
TypeScript
187 lines
6.9 KiB
TypeScript
import {
|
|
SlashCommandBuilder,
|
|
PermissionFlagsBits,
|
|
ChatInputCommandInteraction,
|
|
ChannelType,
|
|
EmbedBuilder,
|
|
TextChannel
|
|
} from 'discord.js';
|
|
import { prisma } from '../database';
|
|
import { SupportedLocale, t } from '../i18n';
|
|
import { logger } from '../utils/logger';
|
|
|
|
export default {
|
|
data: new SlashCommandBuilder()
|
|
.setName('voice')
|
|
.setDescription('Manage temporary voice channels.')
|
|
.setDescriptionLocalizations({
|
|
ko: '임시 음성 채널 설정을 관리합니다.',
|
|
})
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
|
// --- Generator Subcommand ---
|
|
.addSubcommand(subcommand =>
|
|
subcommand
|
|
.setName('generator')
|
|
.setDescription('Configure the voice generator channel.')
|
|
.setDescriptionLocalizations({ ko: '음성 생성기 채널을 설정합니다.' })
|
|
.addStringOption(option =>
|
|
option.setName('action')
|
|
.setDescription('Action to perform')
|
|
.setRequired(true)
|
|
.addChoices(
|
|
{ name: 'set (Set Existing)', value: 'set' },
|
|
{ name: 'create (Create New)', value: 'create' },
|
|
)
|
|
)
|
|
.addChannelOption(option =>
|
|
option.setName('channel')
|
|
.setDescription('Voice channel (for set action)')
|
|
.addChannelTypes(ChannelType.GuildVoice)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('name')
|
|
.setDescription('New channel name (for create action)')
|
|
)
|
|
.addChannelOption(option =>
|
|
option.setName('category')
|
|
.setDescription('Target category for temp channels')
|
|
.addChannelTypes(ChannelType.GuildCategory)
|
|
)
|
|
)
|
|
// --- Settings Subcommand ---
|
|
.addSubcommand(subcommand =>
|
|
subcommand
|
|
.setName('settings')
|
|
.setDescription('Manage default settings for temporary channels.')
|
|
.setDescriptionLocalizations({ ko: '임시 채널의 기본 설정을 관리합니다.' })
|
|
.addStringOption(option =>
|
|
option.setName('action')
|
|
.setDescription('Action to perform')
|
|
.setRequired(true)
|
|
.addChoices(
|
|
{ name: 'name (Template)', value: 'name' },
|
|
{ name: 'limit (User Count)', value: 'limit' },
|
|
{ name: 'status (Check Config)', value: 'status' },
|
|
)
|
|
)
|
|
.addStringOption(option =>
|
|
option.setName('template')
|
|
.setDescription('Naming template (for name action)')
|
|
)
|
|
.addIntegerOption(option =>
|
|
option.setName('limit')
|
|
.setDescription('User limit (for limit action)')
|
|
.setMinValue(0)
|
|
.setMaxValue(99)
|
|
)
|
|
),
|
|
|
|
async execute(interaction: ChatInputCommandInteraction, locale: SupportedLocale) {
|
|
const subcommand = interaction.options.getSubcommand();
|
|
const guildId = interaction.guildId!;
|
|
|
|
try {
|
|
// --- GENERATOR ---
|
|
if (subcommand === 'generator') {
|
|
const action = interaction.options.getString('action', true);
|
|
const category = interaction.options.getChannel('category');
|
|
|
|
if (action === 'set') {
|
|
const channel = interaction.options.getChannel('channel');
|
|
if (!channel) return interaction.reply({ content: '❌ `channel` 옵션을 선택해주세요.', ephemeral: true });
|
|
|
|
await prisma.voiceGenerator.upsert({
|
|
where: { channelId: channel.id },
|
|
update: { categoryId: category?.id || null, guildId },
|
|
create: { channelId: channel.id, guildId, categoryId: category?.id || null }
|
|
});
|
|
return interaction.reply({
|
|
content: t(locale, 'commands.voiceSetup.setSuccess', { channel: `${channel}` }),
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
if (action === 'create') {
|
|
const name = interaction.options.getString('name');
|
|
if (!name) return interaction.reply({ content: '❌ `name` 옵션을 입력해주세요.', ephemeral: true });
|
|
|
|
const newChannel = await interaction.guild!.channels.create({
|
|
name,
|
|
type: ChannelType.GuildVoice,
|
|
parent: category?.id || null,
|
|
});
|
|
await prisma.voiceGenerator.create({
|
|
data: { channelId: newChannel.id, guildId, categoryId: category?.id || null }
|
|
});
|
|
return interaction.reply({
|
|
content: t(locale, 'commands.voiceSetup.createSuccess', { channel: `${newChannel}` }),
|
|
ephemeral: true
|
|
});
|
|
}
|
|
}
|
|
|
|
// --- SETTINGS ---
|
|
if (subcommand === 'settings') {
|
|
const action = interaction.options.getString('action', true);
|
|
|
|
if (action === 'name') {
|
|
const template = interaction.options.getString('template');
|
|
if (!template) return interaction.reply({ content: '❌ `template` 옵션을 입력해주세요.', ephemeral: true });
|
|
|
|
await prisma.voiceGuildConfig.upsert({
|
|
where: { guildId },
|
|
update: { defaultNameTemplate: template },
|
|
create: { guildId, defaultNameTemplate: template }
|
|
});
|
|
return interaction.reply({
|
|
content: t(locale, 'commands.voiceConfig.setSuccess'),
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
if (action === 'limit') {
|
|
const limit = interaction.options.getInteger('limit');
|
|
if (limit === null) return interaction.reply({ content: '❌ `limit` 옵션을 입력해주세요.', ephemeral: true });
|
|
|
|
await prisma.voiceGuildConfig.upsert({
|
|
where: { guildId },
|
|
update: { defaultUserLimit: limit },
|
|
create: { guildId, defaultUserLimit: limit }
|
|
});
|
|
return interaction.reply({
|
|
content: t(locale, 'commands.voiceConfig.setSuccess'),
|
|
ephemeral: true
|
|
});
|
|
}
|
|
|
|
if (action === 'status') {
|
|
const config = await prisma.voiceGuildConfig.findUnique({ where: { guildId } });
|
|
const embed = new EmbedBuilder()
|
|
.setTitle(t(locale, 'commands.voiceConfig.statusTitle'))
|
|
.setColor(0x5865F2)
|
|
.addFields(
|
|
{
|
|
name: t(locale, 'commands.voiceConfig.templateLabel'),
|
|
value: `\`${config?.defaultNameTemplate || t(locale, 'voice.defaultRoomName')}\``,
|
|
inline: true
|
|
},
|
|
{
|
|
name: t(locale, 'commands.voiceConfig.limitLabel'),
|
|
value: t(locale, 'commands.voiceConfig.limitValue', { limit: String(config?.defaultUserLimit ?? 0) }),
|
|
inline: true
|
|
}
|
|
);
|
|
return interaction.reply({ embeds: [embed], ephemeral: true });
|
|
}
|
|
}
|
|
} catch (error) {
|
|
logger.error('Error in voice command', error);
|
|
return interaction.reply({
|
|
content: t(locale, 'errors.E3003.userMessage'),
|
|
ephemeral: true
|
|
});
|
|
}
|
|
},
|
|
};
|
|
|