반응형
예전에 만들어놓고 한동안 방치해놨지만 늦게나마 기록을 위해 코드도 정리할겸 전체 코드를 올리기로 했다.
const { Client, GatewayIntentBits, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } = require('discord.js');
const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, getVoiceConnection } = require('@discordjs/voice');
//const ytdl = require('ytdl-core'); //수정예정
const ytdl = require("@distube/ytdl-core"); //추가 재생안되는바람에 수정
const { token, youtubeApiKey } = require('./discordConfig.js');
const sodium = require('libsodium-wrappers'); // 추가된 부분
const search = require('youtube-search'); // 유튜브 검색 추가
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
]
});
const opts = {
maxResults: 1,
key: youtubeApiKey,
type: 'video'
};
// 준비
client.on('ready', () => console.log(`${client.user.tag} 에 로그인됨`));
// 봇 명령어 구분 문자
const prefix = '!';
let connection;
let voiceChannel;
let player;
let playList = [];
let playListTitle = [];
let playListThumbnail = [];
let isPlaying = false;
let playRepeat = false;
let currentIndex = 0;
let embedMessage = null; // 처음 전송된 embed 메시지를 저장할 변수
//명령어가 !재생인경우
function prefixPlay(message){
const query = message.content.replace('!재생', '').trim();
let playUrl = "";
let title;
let thumbnailUrl;
if (!query) {
return message.reply('재생할 노래 제목이나 URL을 입력하세요.');
}
voiceChannel = message.member.voice.channel;
if (!voiceChannel) {
return message.reply('채널에 먼저 선 입장 필요');
}
const permissions = voiceChannel.permissionsFor(message.client.user);
if (!permissions.has('CONNECT') || !permissions.has('SPEAK')) {
return message.reply('권한이 없습니다.');
}
search(query, opts, async (err, results) => {
if (err) return console.error(err);
if (results.length === 0) {
return message.reply('검색 결과가 없습니다.');
}
playUrl = results[0].link;
title = results[0].title;
thumbnailUrl = results[0].thumbnails.default.url;
playList.push(playUrl);
playListTitle.push(title);
playListThumbnail.push(thumbnailUrl);
if (!isPlaying) {
playNext(voiceChannel, message, title, thumbnailUrl);
} else {
if (embedMessage && (playList.length % 5 === 0)) {
await embedMessage.delete();
sendEmbedMessage(message, title, thumbnailUrl);
}
message.reply(`"${title}"이(가) 재생 목록에 추가됨`);
}
});
}
async function playNext(voiceChannel, message) {
if (playList.length === 0) {
isPlaying = false;
//if (connection) connection.disconnect(); //플레이 리스트가 없다면 자동퇴장
updateEmbedMessage('재생중인 노래가 없습니다', '', '', 'Music Bot (반복재생 off)');
return;
}
if(currentIndex >= playList.length){
if(playRepeat){
currentIndex=0;
}else{
isPlaying = false;
/*
if (connection) {
leaveChannel(message)
}
*/
updateEmbedMessage('재생중인 노래가 없습니다', '', '', 'Music Bot (반복재생 off)');
return;
}
}
isPlaying = true;
const playUrl = playList[currentIndex]; // 재생목록의 현재인덱스로 재생
try {
if (!connection) {
connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: message.guild.id,
adapterCreator: message.guild.voiceAdapterCreator,
});
}
// 원본
/*
const stream = ytdl(playUrl, {
filter: 'audioonly',
fmt: 'mp3',
highWaterMark: 1 << 62,
liveBuffer: 1 << 62,
dlChunkSize: 0,
bitrate: 128,
quality: 'lowestaudio'
});
*/
const stream = ytdl(playUrl, {
filter: 'audioonly',
quality: 'highestaudio',
highWaterMark: 1 << 25, // 스트림 버퍼 크기 설정 (더 크게)
});
const resource = createAudioResource(stream);
if (!player) {
player = createAudioPlayer();
//현재 플레이되는 곡의 재생 종료 이벤트를 받는 구간
player.on(AudioPlayerStatus.Idle, () => {
currentIndex++; //인덱스 증가하여 다음 재생목록 실행
playNext(voiceChannel, message);
});
connection.subscribe(player);
}
player.play(resource);
// 처음에만 embed 메시지를 전송
if (!embedMessage) {
sendEmbedMessage(message, playListTitle[currentIndex], playListThumbnail[currentIndex]);
} else {
updateEmbedMessage('현재 재생 중', playListTitle[currentIndex], playListThumbnail[currentIndex], 'Music Bot (반복재생 off)');
}
} catch (error) {
console.error(error);
}
}
// 처음 embed 메시지 전송
function sendEmbedMessage(message, title, thumbnailUrl) {
const embed = new EmbedBuilder()
.setColor('#0099ff')
.setTitle('현재 재생 중')
.setDescription(`**${title}**`)
.setThumbnail(thumbnailUrl)
.setFooter({ text: 'Music Bot' });
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId('skip')
.setLabel('넘기기')
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId('songList')
.setLabel('재생목록')
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId('stop')
.setLabel('컷')
.setStyle(ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId('loop')
.setLabel('반복 재생')
.setStyle(ButtonStyle.Primary)
);
message.reply({ embeds: [embed], components: [row] }).then(sentMessage => {
embedMessage = sentMessage; // 전송된 메시지를 저장
});
}
// embed 메시지 업데이트
function updateEmbedMessage(title, songTitle, thumbnailUrl, footer) {
if (!embedMessage) return;
const updatedEmbed = new EmbedBuilder()
.setColor('#0099ff')
.setTitle(title)
.setDescription(`**${songTitle}**`)
.setFooter({ text: footer });
if (thumbnailUrl) {
updatedEmbed.setThumbnail(thumbnailUrl);
}
embedMessage.edit({ embeds: [updatedEmbed] });
}
// 버튼 클릭 처리
client.on('interactionCreate', async interaction => {
if (!interaction.isButton()) return;
const customId = interaction.customId;
if (customId === 'skip') {
await interaction.reply({ content: '노래를 스킵합니다!', ephemeral: true });
if (player) {
player.stop();
}
} else if (customId === 'songList') {
if (playList.length === 0) {
return interaction.reply('재생 목록 없음');
}
let response = '재생 목록:\n';
playListTitle.forEach((item, index) => {
response += `[${index}] = ${item}\n`;
});
interaction.reply(response);
} else if (customId === 'stop') {
await interaction.reply({ content: '노래봇이 꺼졌습니다'});
if (embedMessage) {
await embedMessage.delete();
embedMessage = null; // 삭제 후 참조를 초기화
}
isPlaying = false;
if (player) {
player.stop();
}
if (connection) {
leaveChannel()
}
} else if (customId === 'loop') {
await interaction.deferUpdate();
if(!playRepeat){
playRepeat = true;
updateEmbedMessage('현재 재생 중', playListTitle[currentIndex], playListThumbnail[currentIndex], 'Music Bot (반복재생 on)');
}
else if(playRepeat){
playRepeat = false;
updateEmbedMessage('현재 재생 중', playListTitle[currentIndex], playListThumbnail[currentIndex], 'Music Bot (반복재생 off)');
}
}
});
// 채널 나가기
function leaveChannel(){
connection.destroy()
connection = "" // connection 을 null로 만들어야지 다음 join이 됨...
//채널 나갈시 모두 초기화 시킴
player = ""
playList = [];
playListTitle = [];
playListThumbnail = [];
isPlaying = false;
playRepeat = false;
currentIndex = 0;
}
client.on('messageCreate', async message => {
if (message.author.bot) return;
if (!message.content.startsWith(prefix)) return;
if (message.content === '!재생목록'){
if (playList.length === 0) {
return message.reply('재생 목록 없음');
}
let response = '현재 재생 목록:\n';
playListTitle.forEach((item, index) => {
response += `[${index}] = ${item}\n`;
});
message.reply(response);
}
else if (message.content.startsWith('!삭제')) {
const index = parseInt(message.content.replace('!삭제', '').trim(), 10);
if (isNaN(index) || index < 0 || index >= playList.length) {
return message.reply('잘못된 인덱스');
}
let removed = playList.splice(index, 1);
removed = playListTitle.splice(index, 1);
if (index === currentIndex && index === playList.length) {
currentIndex = 0;
} else if (index <= currentIndex) {
currentIndex--;
}
message.reply(`"${removed[0]}" 재생 목록에서 삭제`);
}
else if(message.content === '!넘기기'){
if (player) {
player.stop();
}
}
else if (message.content.startsWith("!재생")) prefixPlay(message);
else if (message.content === '!반복재생'){
if(!playRepeat){
playRepeat = true;
message.reply('반복재생 켜짐');
}
else if(playRepeat){
playRepeat = false;
message.reply('반복재생 꺼짐');
}
}
else if(message.content === '!초기화'){
playList = [];
playListTitle = [];
message.reply('재생목록 초기화');
}
else if(message.content === '!컷' || message.content === '!중지'){
isPlaying = false;
if (player) {
player.stop();
}
if (connection) {
leaveChannel()
}
}
});
client.login(token);
깔끔하게 이전에 안쓰던 코드들 정리하니 마음이 편안~하다
반응형
'디스코드봇' 카테고리의 다른 글
[JS] 디스코드 노래 봇 8 (0) | 2025.02.27 |
---|---|
[JS] 디스코드 노래 봇 6 (1) | 2024.10.17 |
[JS] 디스코드 노래 봇 5 (0) | 2024.08.24 |
[JS] 디스코드 노래 봇 4 (1) | 2024.07.14 |
[JS] 디스코드 노래 봇 3 (0) | 2024.06.30 |