Files
music_bot_v2/bot/src/commands/join.ts
claude-bot d0dcdb1563 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>
2026-04-26 23:13:16 +09:00

93 lines
3.2 KiB
TypeScript

import { Message, ChatInputApplicationCommandData, Guild, ChatInputCommandInteraction, EmbedBuilder, ApplicationCommandOptionType, ChannelType } from "discord.js";
import { client, lavalinkManager } from "../index";
import { Command } from "../types/Command";
import { GuildPlayer } from "../classes/GuildPlayer";
import { getTextChannelAndMsg } from "../utils/music/Channel";
/** join 명령어 */
export default class implements Command {
/** 해당 명령어 설명 */
name = "join";
visible = true;
aliases: string[] = [];
description: string = "음성채널 참가";
metaData: ChatInputApplicationCommandData = {
name: this.name,
description: this.description,
options: [
{
type: ApplicationCommandOptionType.Channel,
name: "channel",
description: "등록할 채널 (선택)",
channel_types: [ChannelType.GuildVoice],
},
{
type: ApplicationCommandOptionType.String,
name: "channel_id",
description: "채널 ID 또는 #멘션 (선택)",
},
]
};
/** 실행되는 부분 */
async slashRun(interaction: ChatInputCommandInteraction) {
const channel = interaction.options.getChannel("channel");
const channelId = interaction.options.getString("channel_id");
await interaction.editReply({ embeds: [ (await channelJoin(interaction.guild, channel?.id || channelId)).embed ] });
return;
}
async messageRun(message: Message) {
if (message.channel?.type !== ChannelType.GuildText) return;
return;
// return await message.channel.send({ content: "예시 명령어" }).then(m => client.msgDelete(m, 5));
}
}
export async function channelJoin(guild: Guild | null, voiceChannelId: string | null): Promise<{
embed: EmbedBuilder;
player?: GuildPlayer;
}> {
if (!guild) return { embed: client.mkembed({
title: "guild를 가져올수 없습니다.",
color: "DarkRed",
}) };
const { channel, msg, reason } = await getTextChannelAndMsg(guild);
if (reason || !channel || !msg) return { embed: client.mkembed({
title: reason ?? "오류발생",
color: "DarkRed",
}) };
if (!voiceChannelId) return { embed: client.mkembed({
title: "채널 아이디 가져오기 오류",
color: "DarkRed",
}) };
const voiceChannel = guild.channels.cache.get(voiceChannelId.replace(/\<|\#|\!|\>/g,"").trim());
if (!voiceChannel?.id) return { embed: client.mkembed({
title: `${voiceChannelId} 채널 가져오기 오류`,
color: "DarkRed",
}) };
if (voiceChannel.type !== ChannelType.GuildVoice) return { embed: client.mkembed({
title: `<#${voiceChannelId}> 음성 채널이 아닙니다.`,
color: "DarkRed",
}) };
let player = lavalinkManager.getPlayer(guild.id);
if (player) return { embed: client.mkembed({ title: `이미 <#${player.voiceChannelId}> 참가중입니다.` }), player };
player = new GuildPlayer(
guild,
await lavalinkManager.shoukaku.joinVoiceChannel({
guildId: guild.id,
channelId: voiceChannel.id,
shardId: guild.shardId,
deaf: true,
mute: false,
}),
voiceChannel.id,
channel,
msg,
);
lavalinkManager.addPlayer(guild.id, player);
return { embed: client.mkembed({
title: `<#${voiceChannelId}> 참가 완료`,
}), player };
}