feat: Add an audit log category selection step to the setup wizard, shifting subsequent steps and updating related logic and translations.
This commit is contained in:
parent
bdd91f6737
commit
b81bc6b146
|
|
@ -35,26 +35,34 @@
|
||||||
- **컴포넌트**: 점검 통과 상태 (✅ 모두 정상 / ⚠️ 일부 부족). 부족한 경우 권한 부여를 안내합니다. (권한 검사 모듈 `/audit-permissions`의 축소판)
|
- **컴포넌트**: 점검 통과 상태 (✅ 모두 정상 / ⚠️ 일부 부족). 부족한 경우 권한 부여를 안내합니다. (권한 검사 모듈 `/audit-permissions`의 축소판)
|
||||||
- **액션**: `[다시 검사]` / `[다음]` 버튼.
|
- **액션**: `[다시 검사]` / `[다음]` 버튼.
|
||||||
|
|
||||||
### Step 3: 감사 채널 설정 (Audit Channel)
|
### Step 3: 감사 로그 채널 설정 (Audit Channel)
|
||||||
|
|
||||||
- **내용**: 봇의 주요 이벤트와 에러 로그를 남길 시스템 통보 채널을 지정합니다.
|
- **내용**: 봇의 주요 이벤트와 에러 로그를 남길 시스템 통보 채널을 지정합니다.
|
||||||
- **컴포넌트**:
|
- **컴포넌트**:
|
||||||
- 텍스트 채널을 선택하는 `ChannelSelectMenu` (ChannelType.GuildText 채널만).
|
- 텍스트 채널을 선택하는 `ChannelSelectMenu` (ChannelType.GuildText 채널만).
|
||||||
- `[사용 안함(비활성화)]` 버튼.
|
- `[사용 안함(비활성화)]` 버튼.
|
||||||
- **액션**: 채널 선택 시 DB 갱신 후, `[다음]` 버튼을 눌러 이동 (혹은 선택 즉시 자동 다음 이동 고려).
|
- **액션**: 채널 선택 시 DB 갱신 후, Step 4로 이동 (사용 안함 선택 시 Step 5로 이동).
|
||||||
|
|
||||||
### Step 4: 임시 음성 채널 설정 (Voice Generator)
|
### Step 4: 감사 로그 카테고리 설정 (Audit Categories)
|
||||||
|
|
||||||
|
- **내용**: 수신할 감사 로그의 종류(음성, 권한, 시스템 등)를 필터링합니다.
|
||||||
|
- **컴포넌트**:
|
||||||
|
- 각 카테고리(SYSTEM, VOICE, PERMISSION, INVITE, MIMIC)를 토글할 수 있는 버튼 5개.
|
||||||
|
- 활성화 상태는 **초록색(Success)**, 비활성화는 **빨간색(Danger)**으로 표시.
|
||||||
|
- **액션**: 버튼 클릭 시 DB의 `disabledCategories` 필드 업데이트 후 현재 뷰 갱신. `[다음 단계]` 버튼으로 이동.
|
||||||
|
|
||||||
|
### Step 5: 임시 음성 채널 설정 (Voice Generator)
|
||||||
|
|
||||||
- **내용**: 임시 음성 채널 생성 시스템의 진입점이 될 '생성기 채널'을 지정하거나 새로 생성합니다.
|
- **내용**: 임시 음성 채널 생성 시스템의 진입점이 될 '생성기 채널'을 지정하거나 새로 생성합니다.
|
||||||
- **컴포넌트**:
|
- **컴포넌트**:
|
||||||
- 기존 생성할 채널을 고르는 `ChannelSelectMenu` (ChannelType.GuildVoice).
|
- 기존 생성할 채널을 고르는 `ChannelSelectMenu` (ChannelType.GuildVoice).
|
||||||
- `[자동 생성]` 버튼: 봇이 새 음성 카테고리와 "➕ 음성 채널 생성" 채널을 자동으로 만들어 줌.
|
- `[자동 생성]` 버튼: 봇이 새 음성 카테고리와 "➕ 음성 채널 생성" 채널을 자동으로 만들어 줌.
|
||||||
- `[건너뛰기]` 버튼.
|
- `[건너뛰기]` 버튼.
|
||||||
- **액션**: 설정 시 즉각 시스템 구동. 이후 `[다음(완료)]` 클릭.
|
- **액션**: 설정 시 즉각 시스템 구동. 이후 Step 6(완료 요약)으로 이동.
|
||||||
|
|
||||||
### Step 5: 설정 요약 (Summary)
|
### Step 6: 설정 요약 (Summary)
|
||||||
|
|
||||||
- **내용**: 지금까지 설정된 모든 항목의 최종 상태를 요약하여 보여줍니다.
|
- **내용**: 지금까지 설정된 모든 항목(내용, 감사 채널/카테고리, 음성 채널)의 최종 상태를 요약하여 보여줍니다.
|
||||||
- **컴포넌트**: 설정 결과 요약 Embed.
|
- **컴포넌트**: 설정 결과 요약 Embed.
|
||||||
- **액션**: `[설정 마치기]` 버튼 (누르면 "설정이 완료되었습니다"로 메시지 변경 후 버튼 비활성화).
|
- **액션**: `[설정 마치기]` 버튼 (누르면 "설정이 완료되었습니다"로 메시지 변경 후 버튼 비활성화).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,11 @@ export const en: TranslationSchema = {
|
||||||
nextBtn: 'Next Step'
|
nextBtn: 'Next Step'
|
||||||
},
|
},
|
||||||
step4: {
|
step4: {
|
||||||
|
title: '3-1️⃣ Audit Log Categories',
|
||||||
|
desc: 'Select which log categories to receive. **Green** buttons are enabled, **Red** buttons are disabled.',
|
||||||
|
nextBtn: 'Next Step',
|
||||||
|
},
|
||||||
|
step5: {
|
||||||
title: '4️⃣ Temporary Voice Channel Setup',
|
title: '4️⃣ Temporary Voice Channel Setup',
|
||||||
desc: 'Select the "Generator Channel" for temporary voice channels.\nYou can choose an existing channel or have the bot **auto-create** a new category and channel.',
|
desc: 'Select the "Generator Channel" for temporary voice channels.\nYou can choose an existing channel or have the bot **auto-create** a new category and channel.',
|
||||||
placeholder: 'Select Generator Channel',
|
placeholder: 'Select Generator Channel',
|
||||||
|
|
@ -199,15 +204,22 @@ export const en: TranslationSchema = {
|
||||||
skipBtn: 'Disable Temp Voice',
|
skipBtn: 'Disable Temp Voice',
|
||||||
nextBtn: 'Finish Setup'
|
nextBtn: 'Finish Setup'
|
||||||
},
|
},
|
||||||
step5: {
|
step6: {
|
||||||
title: '🎉 Setup Summary',
|
title: '🎉 Setup Summary',
|
||||||
desc: '**1. Language**: {{lang}}\n**2. Audit Channel**: {{audit}}\n**3. Temp Voice**: {{voice}}',
|
desc: '**1. Language**: {{lang}}\n**2. Audit Channel**: {{audit}}\n**3. Audit Categories**: {{categories}}\n**4. Temp Voice**: {{voice}}',
|
||||||
finishBtn: 'Done'
|
finishBtn: 'Done'
|
||||||
},
|
},
|
||||||
finished: '✅ The setup wizard has been finished.',
|
finished: '✅ The setup wizard has been finished.',
|
||||||
expired: '⏳ The session has expired. Please run `/setup` again.',
|
expired: '⏳ The session has expired. Please run `/setup` again.',
|
||||||
defaultCategoryName: 'Voice Channels',
|
defaultCategoryName: 'Voice Channels',
|
||||||
defaultGeneratorName: '➕ Create Channel',
|
defaultGeneratorName: '➕ Create Channel',
|
||||||
|
auditCategories: {
|
||||||
|
SYSTEM: 'System',
|
||||||
|
VOICE: 'Voice',
|
||||||
|
PERMISSION: 'Permission',
|
||||||
|
INVITE: 'Invite',
|
||||||
|
MIMIC: 'Mimic',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,11 @@ export const ko: TranslationSchema = {
|
||||||
nextBtn: '다음 단계'
|
nextBtn: '다음 단계'
|
||||||
},
|
},
|
||||||
step4: {
|
step4: {
|
||||||
|
title: '3-1️⃣ 감사 로그 카테고리',
|
||||||
|
desc: '수신할 로그 카테고리를 선택하세요. 버튼 색상이 **초록색**이면 수신, **빨간색**이면 차단 상태입니다.',
|
||||||
|
nextBtn: '다음 단계',
|
||||||
|
},
|
||||||
|
step5: {
|
||||||
title: '4️⃣ 임시 음성 채널 설정',
|
title: '4️⃣ 임시 음성 채널 설정',
|
||||||
desc: '임시 음성 채널을 생성할 "생성기 채널"을 선택해주세요.\n기존의 채널을 고르거나 카테고리/채널을 봇이 **자동 생성**하게 할 수도 있습니다.',
|
desc: '임시 음성 채널을 생성할 "생성기 채널"을 선택해주세요.\n기존의 채널을 고르거나 카테고리/채널을 봇이 **자동 생성**하게 할 수도 있습니다.',
|
||||||
placeholder: '생성기로 쓸 음성 채널 선택',
|
placeholder: '생성기로 쓸 음성 채널 선택',
|
||||||
|
|
@ -199,15 +204,22 @@ export const ko: TranslationSchema = {
|
||||||
skipBtn: '임시 음성 사용 안함',
|
skipBtn: '임시 음성 사용 안함',
|
||||||
nextBtn: '설정 완료'
|
nextBtn: '설정 완료'
|
||||||
},
|
},
|
||||||
step5: {
|
step6: {
|
||||||
title: '🎉 설정 완료 요약',
|
title: '🎉 설정 완료 요약',
|
||||||
desc: '**1. 언어**: {{lang}}\n**2. 감사 채널**: {{audit}}\n**3. 임시 음성 채널**: {{voice}}',
|
desc: '**1. 언어**: {{lang}}\n**2. 감사 채널**: {{audit}}\n**3. 감사 카테고리**: {{categories}}\n**4. 임시 음성 채널**: {{voice}}',
|
||||||
finishBtn: '마치기'
|
finishBtn: '마치기'
|
||||||
},
|
},
|
||||||
finished: '✅ 설정 마법사를 종료했습니다.',
|
finished: '✅ 설정 마법사를 종료했습니다.',
|
||||||
expired: '⏳ 시간이 만료되었습니다. `/setup`을 다시 실행해주세요.',
|
expired: '⏳ 시간이 만료되었습니다. `/setup`을 다시 실행해주세요.',
|
||||||
defaultCategoryName: '음성 채널',
|
defaultCategoryName: '음성 채널',
|
||||||
defaultGeneratorName: '➕ 채널 생성하기',
|
defaultGeneratorName: '➕ 채널 생성하기',
|
||||||
|
auditCategories: {
|
||||||
|
SYSTEM: '시스템',
|
||||||
|
VOICE: '음성',
|
||||||
|
PERMISSION: '권한',
|
||||||
|
INVITE: '초대',
|
||||||
|
MIMIC: '흉내',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,12 +123,20 @@ export interface TranslationSchema {
|
||||||
step1: { title: string; desc: string; placeholder: string; nextBtn: string; skipBtn: string; };
|
step1: { title: string; desc: string; placeholder: string; nextBtn: string; skipBtn: string; };
|
||||||
step2: { title: string; descOk: string; descFail: string; recheckBtn: string; nextBtn: string; };
|
step2: { title: string; descOk: string; descFail: string; recheckBtn: string; nextBtn: string; };
|
||||||
step3: { title: string; desc: string; placeholder: string; disableBtn: string; nextBtn: string; };
|
step3: { title: string; desc: string; placeholder: string; disableBtn: string; nextBtn: string; };
|
||||||
step4: { title: string; desc: string; placeholder: string; autoBtn: string; skipBtn: string; nextBtn: string; };
|
step4: { title: string; desc: string; nextBtn: string; };
|
||||||
step5: { title: string; desc: string; finishBtn: string; };
|
step5: { title: string; desc: string; placeholder: string; autoBtn: string; skipBtn: string; nextBtn: string; };
|
||||||
|
step6: { title: string; desc: string; finishBtn: string; };
|
||||||
finished: string;
|
finished: string;
|
||||||
expired: string;
|
expired: string;
|
||||||
defaultCategoryName: string;
|
defaultCategoryName: string;
|
||||||
defaultGeneratorName: string;
|
defaultGeneratorName: string;
|
||||||
|
auditCategories: {
|
||||||
|
SYSTEM: string;
|
||||||
|
VOICE: string;
|
||||||
|
PERMISSION: string;
|
||||||
|
INVITE: string;
|
||||||
|
MIMIC: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,29 @@ export async function handleSetupWizardInteraction(interaction: MessageComponent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 4 Toggle: Audit Category
|
||||||
|
if (customId.startsWith('setup_audit_toggle_')) {
|
||||||
|
const category = customId.replace('setup_audit_toggle_', '');
|
||||||
|
const audit = await prisma.auditChannel.findUnique({ where: { guildId: interaction.guildId! } });
|
||||||
|
if (!audit) return;
|
||||||
|
|
||||||
|
let disabled = [...audit.disabledCategories];
|
||||||
|
if (disabled.includes(category)) {
|
||||||
|
disabled = disabled.filter(c => c !== category);
|
||||||
|
} else {
|
||||||
|
disabled.push(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.auditChannel.update({
|
||||||
|
where: { guildId: interaction.guildId! },
|
||||||
|
data: { disabledCategories: disabled }
|
||||||
|
});
|
||||||
|
|
||||||
|
const { embed, components } = await SetupWizardRenderer.renderStep(4, interaction, locale);
|
||||||
|
await interaction.update({ embeds: [embed], components });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 1: Language Select
|
// Step 1: Language Select
|
||||||
if (customId === 'setup_lang_select' && interaction.isStringSelectMenu()) {
|
if (customId === 'setup_lang_select' && interaction.isStringSelectMenu()) {
|
||||||
const selectedLocale = interaction.values[0] as SupportedLocale;
|
const selectedLocale = interaction.values[0] as SupportedLocale;
|
||||||
|
|
@ -57,14 +80,15 @@ export async function handleSetupWizardInteraction(interaction: MessageComponent
|
||||||
create: { guildId: interaction.guildId!, channelId }
|
create: { guildId: interaction.guildId!, channelId }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto proceed to next step
|
// Auto proceed to next step (Step 4: Categories)
|
||||||
const { embed, components } = await SetupWizardRenderer.renderStep(4, interaction, locale);
|
const { embed, components } = await SetupWizardRenderer.renderStep(4, interaction, locale);
|
||||||
await interaction.update({ embeds: [embed], components });
|
await interaction.update({ embeds: [embed], components });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (customId === 'setup_audit_disable') {
|
if (customId === 'setup_audit_disable') {
|
||||||
await prisma.auditChannel.delete({ where: { guildId: interaction.guildId! } }).catch(() => {});
|
await prisma.auditChannel.delete({ where: { guildId: interaction.guildId! } }).catch(() => {});
|
||||||
const { embed, components } = await SetupWizardRenderer.renderStep(4, interaction, locale);
|
// Skip categories if disabled, go to Step 5: Voice Setup
|
||||||
|
const { embed, components } = await SetupWizardRenderer.renderStep(5, interaction, locale);
|
||||||
await interaction.update({ embeds: [embed], components });
|
await interaction.update({ embeds: [embed], components });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +113,7 @@ export async function handleSetupWizardInteraction(interaction: MessageComponent
|
||||||
create: { channelId: channel.id, guildId: interaction.guildId!, categoryId: channel.parentId }
|
create: { channelId: channel.id, guildId: interaction.guildId!, categoryId: channel.parentId }
|
||||||
});
|
});
|
||||||
|
|
||||||
const { embed, components } = await SetupWizardRenderer.renderStep(5, interaction, locale);
|
const { embed, components } = await SetupWizardRenderer.renderStep(6, interaction, locale);
|
||||||
await interaction.update({ embeds: [embed], components });
|
await interaction.update({ embeds: [embed], components });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +147,7 @@ export async function handleSetupWizardInteraction(interaction: MessageComponent
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { embed, components } = await SetupWizardRenderer.renderStep(5, interaction, locale);
|
const { embed, components } = await SetupWizardRenderer.renderStep(6, interaction, locale);
|
||||||
await interaction.editReply({ embeds: [embed], components });
|
await interaction.editReply({ embeds: [embed], components });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if ((e as Error).message.includes('Missing Permissions')) {
|
if ((e as Error).message.includes('Missing Permissions')) {
|
||||||
|
|
@ -136,7 +160,7 @@ export async function handleSetupWizardInteraction(interaction: MessageComponent
|
||||||
|
|
||||||
if (customId === 'setup_voice_disable') {
|
if (customId === 'setup_voice_disable') {
|
||||||
await prisma.voiceGenerator.deleteMany({ where: { guildId: interaction.guildId! } });
|
await prisma.voiceGenerator.deleteMany({ where: { guildId: interaction.guildId! } });
|
||||||
const { embed, components } = await SetupWizardRenderer.renderStep(5, interaction, locale);
|
const { embed, components } = await SetupWizardRenderer.renderStep(6, interaction, locale);
|
||||||
await interaction.update({ embeds: [embed], components });
|
await interaction.update({ embeds: [embed], components });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,26 +124,57 @@ export class SetupWizardRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 4: {
|
case 4: {
|
||||||
|
const audit = await prisma.auditChannel.findUnique({ where: { guildId: interaction.guildId! } });
|
||||||
|
const disabled = audit?.disabledCategories || [];
|
||||||
|
|
||||||
embed.setTitle(t(locale, 'commands.setup.step4.title'))
|
embed.setTitle(t(locale, 'commands.setup.step4.title'))
|
||||||
.setDescription(t(locale, 'commands.setup.step4.desc'));
|
.setDescription(t(locale, 'commands.setup.step4.desc'));
|
||||||
|
|
||||||
|
const categories: ('SYSTEM' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC')[] = ['SYSTEM', 'VOICE', 'PERMISSION', 'INVITE', 'MIMIC'];
|
||||||
|
const row1 = new ActionRowBuilder<ButtonBuilder>();
|
||||||
|
|
||||||
|
categories.forEach(cat => {
|
||||||
|
const isEnabled = !disabled.includes(cat);
|
||||||
|
row1.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`setup_audit_toggle_${cat}`)
|
||||||
|
.setLabel(t(locale, `commands.setup.auditCategories.${cat}`))
|
||||||
|
.setStyle(isEnabled ? ButtonStyle.Success : ButtonStyle.Danger)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const row2 = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('setup_next_5')
|
||||||
|
.setLabel(t(locale, 'commands.setup.step4.nextBtn'))
|
||||||
|
.setStyle(ButtonStyle.Primary)
|
||||||
|
);
|
||||||
|
|
||||||
|
components.push(row1, row2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 5: {
|
||||||
|
embed.setTitle(t(locale, 'commands.setup.step5.title'))
|
||||||
|
.setDescription(t(locale, 'commands.setup.step5.desc'));
|
||||||
|
|
||||||
const select = new ChannelSelectMenuBuilder()
|
const select = new ChannelSelectMenuBuilder()
|
||||||
.setCustomId('setup_voice_select')
|
.setCustomId('setup_voice_select')
|
||||||
.setPlaceholder(t(locale, 'commands.setup.step4.placeholder'))
|
.setPlaceholder(t(locale, 'commands.setup.step5.placeholder'))
|
||||||
.setChannelTypes(ChannelType.GuildVoice);
|
.setChannelTypes(ChannelType.GuildVoice);
|
||||||
|
|
||||||
const btnRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
const btnRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
.setCustomId('setup_voice_auto')
|
.setCustomId('setup_voice_auto')
|
||||||
.setLabel(t(locale, 'commands.setup.step4.autoBtn'))
|
.setLabel(t(locale, 'commands.setup.step5.autoBtn'))
|
||||||
.setStyle(ButtonStyle.Success),
|
.setStyle(ButtonStyle.Success),
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
.setCustomId('setup_voice_disable')
|
.setCustomId('setup_voice_disable')
|
||||||
.setLabel(t(locale, 'commands.setup.step4.skipBtn'))
|
.setLabel(t(locale, 'commands.setup.step5.skipBtn'))
|
||||||
.setStyle(ButtonStyle.Danger),
|
.setStyle(ButtonStyle.Danger),
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
.setCustomId('setup_next_5')
|
.setCustomId('setup_next_6')
|
||||||
.setLabel(t(locale, 'commands.setup.step4.nextBtn'))
|
.setLabel(t(locale, 'commands.setup.step5.nextBtn'))
|
||||||
.setStyle(ButtonStyle.Secondary)
|
.setStyle(ButtonStyle.Secondary)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -152,30 +183,39 @@ export class SetupWizardRenderer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 5: {
|
case 6: {
|
||||||
if (!interaction.guild) throw new Error('Guild not found');
|
if (!interaction.guild) throw new Error('Guild not found');
|
||||||
|
|
||||||
const config = await prisma.guildConfig.findUnique({ where: { guildId: interaction.guild.id } });
|
const config = await prisma.guildConfig.findUnique({ where: { guildId: interaction.guild.id } });
|
||||||
const audit = await prisma.auditChannel.findUnique({ where: { guildId: interaction.guild.id } });
|
const audit = await prisma.auditChannel.findUnique({ where: { guildId: interaction.guild.id } });
|
||||||
const voice = await prisma.voiceGenerator.findFirst({ where: { guildId: interaction.guild.id } });
|
const voice = await prisma.voiceGenerator.findFirst({ where: { guildId: interaction.guild.id } });
|
||||||
|
|
||||||
embed.setTitle(t(locale, 'commands.setup.step5.title'))
|
embed.setTitle(t(locale, 'commands.setup.step6.title'))
|
||||||
.setColor(Colors.Green);
|
.setColor(Colors.Green);
|
||||||
|
|
||||||
const langStr = config?.locale === 'ko' ? 'Korean' : 'English';
|
const langStr = config?.locale === 'ko' ? 'Korean' : 'English';
|
||||||
const auditStr = audit?.channelId ? `<#${audit.channelId}>` : 'Disabled';
|
const auditStr = audit?.channelId ? `<#${audit.channelId}>` : 'Disabled';
|
||||||
const voiceStr = voice?.channelId ? `<#${voice.channelId}>` : 'Disabled';
|
const voiceStr = voice?.channelId ? `<#${voice.channelId}>` : 'Disabled';
|
||||||
|
|
||||||
embed.setDescription(t(locale, 'commands.setup.step5.desc', {
|
// 감사 로그 카테고리 요약
|
||||||
|
let catStr = 'None';
|
||||||
|
if (audit?.channelId) {
|
||||||
|
const allCats: ('SYSTEM' | 'VOICE' | 'PERMISSION' | 'INVITE' | 'MIMIC')[] = ['SYSTEM', 'VOICE', 'PERMISSION', 'INVITE', 'MIMIC'];
|
||||||
|
const enabled = allCats.filter(c => !audit.disabledCategories.includes(c));
|
||||||
|
catStr = enabled.map(c => t(locale, `commands.setup.auditCategories.${c}`)).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
embed.setDescription(t(locale, 'commands.setup.step6.desc', {
|
||||||
lang: langStr,
|
lang: langStr,
|
||||||
audit: auditStr,
|
audit: auditStr,
|
||||||
|
categories: catStr,
|
||||||
voice: voiceStr
|
voice: voiceStr
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const btnRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
const btnRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
.setCustomId('setup_finish')
|
.setCustomId('setup_finish')
|
||||||
.setLabel(t(locale, 'commands.setup.step5.finishBtn'))
|
.setLabel(t(locale, 'commands.setup.step6.finishBtn'))
|
||||||
.setStyle(ButtonStyle.Success)
|
.setStyle(ButtonStyle.Success)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue