bot 전체 코드 품질 개선 및 버그 수정

- GuildPlayer: 타이머 레이스 컨디션 수정, 모든 타이머 정리 로직 통합 (clearAllTimers)
- GuildPlayer: 이벤트 핸들러에 try-catch 추가 (end, exception, stuck)
- GuildPlayer: start 이벤트에서 endTimer 정리, autoPlay tracks 길이 검증 추가
- RedisClient: player_seek, player_volume에 누락된 return ���가
- RedisClient: queue_remove 인덱스 검증 주석 명확화
- Handler: runCommand에 try-catch 추가하여 에러 시 사용자에게 응답
- Channel: getGuildById에 누락된 await 추가, getMemberById/getVoiceChannelById 안전한 에러 처리
- Command.d.ts: 잘못된 타입 ChatInputChatInputCommandInteraction → ChatInputCommandInteraction 수정
- join.ts: 채널 멘션 닫는 괄호 누락 수정
- shuffle.ts: 제네릭 타입 적용, 불필요한 5회 반복 제거
- import 경로 대소문자 수정 (Shuffle → shuffle) - Linux 호환
- YoutubeMusic/Spotify: 하드코딩된 IP를 환경변수로 분리
- console.log/error → Logger 통일 (YoutubeMusic, Button, channel)
- interactionCreate: 전체 try-catch 추가, silent catch에 로깅 추가
- Database: schema 경로 __dirname 기반으로 수정, 컬럼 화이트리스트 추가
- 사용하지 않는 코드 정리 (axios 의존성, 주석처리된 user 관련 코드)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-26 23:13:16 +09:00
parent fefdf1fcdb
commit d0dcdb1563
19 changed files with 178 additions and 162 deletions

View File

@@ -1,36 +1,47 @@
import { Interaction, MessageFlags } from "discord.js";
import { handler } from "../index";
import { buttonInteraction } from "../utils/music/Button";
import { Logger } from "../utils/Logger";
export const interactionCreate = async (interaction: Interaction) => {
if (interaction.isStringSelectMenu()) {
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch(() => {});
const commandName = interaction.customId;
const args = interaction.values;
const command = handler.commands.get(commandName);
if (command?.menuRun) return await command.menuRun(interaction, args);
try {
if (interaction.isStringSelectMenu()) {
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch((err) => {
Logger.warn(`[Interaction] SelectMenu deferReply 실패: ${String(err)}`);
});
const commandName = interaction.customId;
const args = interaction.values;
const command = handler.commands.get(commandName);
if (command?.menuRun) return await command.menuRun(interaction, args);
}
if (interaction.isButton()) {
const args = interaction.customId.split("-");
if (!args || args.length === 0) return;
if (args[0] === "music") return buttonInteraction(interaction, args.slice(1));
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch((err) => {
Logger.warn(`[Interaction] Button deferReply 실패: ${String(err)}`);
});
const key = args.shift();
if (!key) return;
const command = handler.commands.get(key);
if (command?.buttonRun) return command.buttonRun(interaction, args);
}
if (!interaction.isChatInputCommand()) return;
/**
* 명령어 친사람만 보이게 설정
* flags: MessageFlags.Ephemeral
*/
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch((err) => {
Logger.warn(`[Interaction] Command deferReply 실패: ${String(err)}`);
});
await handler.runCommand(interaction);
} catch (err) {
Logger.error(`[Interaction] 처리 중 에러: ${String(err)}`);
}
if (interaction.isButton()) {
const args = interaction.customId.split("-");
if (!args || args.length === 0) return;
if (args[0] === "music") return buttonInteraction(interaction, args.slice(1));
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch(() => {});
const key = args.shift();
if (!key) return;
const command = handler.commands.get(key);
if (command?.buttonRun) return command.buttonRun(interaction, args);
}
if (!interaction.isChatInputCommand()) return;
/**
* 명령어 친사람만 보이게 설정
* flags: MessageFlags.Ephemeral
*/
await interaction.deferReply({ flags: MessageFlags.Ephemeral }).catch(() => {});
handler.runCommand(interaction);
}

View File

@@ -1,5 +1,3 @@
// TODO: 음성 상태 변경 이벤트 핸들러 (추후 구현)
// import { VoiceState } from "discord.js";
// import { client } from "../index";
// export const voiceStateUpdate = async (oldState: VoiceState, newState: VoiceState): Promise<void> => {
// }
// export const voiceStateUpdate = async (oldState: VoiceState, newState: VoiceState): Promise<void> => {}