First push
This commit is contained in:
commit
eb75bb1a32
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
cookies.txt
|
||||||
|
config.json
|
||||||
|
package-lock.json
|
|
@ -0,0 +1,63 @@
|
||||||
|
const { addToQueue, playNextInQueue } = require('../utils/queueManager');
|
||||||
|
const ytDlpExec = require('yt-dlp-exec');
|
||||||
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'play',
|
||||||
|
description: 'Play a song from YouTube',
|
||||||
|
async execute(message, args) {
|
||||||
|
const searchQuery = args.join(' ');
|
||||||
|
const voiceChannel = message.member.voice.channel;
|
||||||
|
|
||||||
|
if (!voiceChannel) {
|
||||||
|
return message.reply('You need to be in a voice channel to play music!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!searchQuery) {
|
||||||
|
return message.reply('Please provide a YouTube link or a song name.');
|
||||||
|
}
|
||||||
|
|
||||||
|
let url;
|
||||||
|
if (isValidURL(searchQuery)) {
|
||||||
|
url = searchQuery;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const searchResult = await ytDlpExec(`ytsearch:${searchQuery}`, {
|
||||||
|
dumpSingleJson: true,
|
||||||
|
noPlaylist: true,
|
||||||
|
format: 'bestaudio/best',
|
||||||
|
quiet: true,
|
||||||
|
});
|
||||||
|
url = searchResult.webpage_url;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('yt-dlp search error:', error);
|
||||||
|
return message.reply('Could not find the song. Please try again.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tempFilePath = path.join(__dirname, '../utils/tmp', `${uuidv4()}.mp3`);
|
||||||
|
try {
|
||||||
|
await ytDlpExec(url, {
|
||||||
|
cookies: path.join(__dirname, '../cookies.txt'),
|
||||||
|
format: 'bestaudio',
|
||||||
|
output: tempFilePath,
|
||||||
|
quiet: true,
|
||||||
|
});
|
||||||
|
addToQueue(message.guild.id, tempFilePath);
|
||||||
|
playNextInQueue(message.guild.id, voiceChannel);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('yt-dlp error:', error);
|
||||||
|
message.reply('Failed to download video with yt-dlp.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function isValidURL(string) {
|
||||||
|
try {
|
||||||
|
new URL(string);
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
const { getQueue } = require('../utils/queueManager');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'queue',
|
||||||
|
execute(message) {
|
||||||
|
const queue = getQueue(message.guild.id);
|
||||||
|
if (queue.length === 0) {
|
||||||
|
return message.channel.send('The queue is empty!');
|
||||||
|
}
|
||||||
|
const queueString = queue.map((track, index) => `${index + 1}. ${track.title}`).join('\n');
|
||||||
|
message.channel.send(`Current queue:\n${queueString}`);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
const { skipTrack } = require('../utils/queueManager');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'skip',
|
||||||
|
execute(message) {
|
||||||
|
skipTrack(message.guild.id);
|
||||||
|
message.channel.send('Skipped the current track!');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"prefix": "+",
|
||||||
|
"token": "token",
|
||||||
|
"cookie-file": "/home/user/dz-musicbot/cookies.txt"
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
const { Client, GatewayIntentBits } = require('discord.js');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { prefix, token } = require('./config.json');
|
||||||
|
|
||||||
|
const client = new Client({ intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent] });
|
||||||
|
|
||||||
|
client.commands = new Map();
|
||||||
|
|
||||||
|
const tmpDir = path.join(__dirname, 'utils', 'tmp');
|
||||||
|
if (!fs.existsSync(tmpDir)) {
|
||||||
|
fs.mkdirSync(tmpDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readdir(tmpDir, (err, files) => {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.endsWith('.mp3')) {
|
||||||
|
fs.unlink(path.join(tmpDir, file), err => {
|
||||||
|
if (err) throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const commandFiles = fs.readdirSync(path.join(__dirname, 'commands')).filter(file => file.endsWith('.js'));
|
||||||
|
for (const file of commandFiles) {
|
||||||
|
const command = require(`./commands/${file}`);
|
||||||
|
client.commands.set(command.name, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.once('ready', () => {
|
||||||
|
console.log(`Logged in as ${client.user.tag}!`);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('messageCreate', async message => {
|
||||||
|
if (!message.content.startsWith(prefix) || message.author.bot) return;
|
||||||
|
|
||||||
|
const args = message.content.slice(prefix.length).trim().split(/ +/);
|
||||||
|
const commandName = args.shift().toLowerCase();
|
||||||
|
|
||||||
|
if (!client.commands.has(commandName)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.commands.get(commandName).execute(message, args);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
message.reply('There was an error trying to execute that command!');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.login(token);
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@discordjs/voice": "^0.17.0",
|
||||||
|
"discord.js": "^14.15.3",
|
||||||
|
"ffmpeg-static": "^5.2.0",
|
||||||
|
"libsodium-wrappers": "^0.7.15",
|
||||||
|
"opusscript": "^0.0.8",
|
||||||
|
"prism-media": "^1.3.5",
|
||||||
|
"uuid": "^10.0.0",
|
||||||
|
"yt-dlp-exec": "^1.0.2",
|
||||||
|
"ytdl-core": "^4.11.5"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
const { createAudioPlayer, createAudioResource, AudioPlayerStatus, joinVoiceChannel } = require('@discordjs/voice');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
const queueMap = new Map();
|
||||||
|
|
||||||
|
function addToQueue(guildId, filePath) {
|
||||||
|
if (!queueMap.has(guildId)) {
|
||||||
|
queueMap.set(guildId, []);
|
||||||
|
}
|
||||||
|
queueMap.get(guildId).push(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueue(guildId) {
|
||||||
|
return queueMap.get(guildId) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function playNextInQueue(guildId, voiceChannel) {
|
||||||
|
const queue = getQueue(guildId);
|
||||||
|
if (queue.length === 0) return false;
|
||||||
|
|
||||||
|
const connection = joinVoiceChannel({
|
||||||
|
channelId: voiceChannel.id,
|
||||||
|
guildId: guildId,
|
||||||
|
adapterCreator: voiceChannel.guild.voiceAdapterCreator,
|
||||||
|
});
|
||||||
|
|
||||||
|
const audioPlayer = createAudioPlayer();
|
||||||
|
|
||||||
|
const filePath = queue.shift();
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
console.error('Audio file not found:', filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resource = createAudioResource(filePath);
|
||||||
|
audioPlayer.play(resource);
|
||||||
|
connection.subscribe(audioPlayer);
|
||||||
|
|
||||||
|
audioPlayer.on(AudioPlayerStatus.Idle, () => {
|
||||||
|
if (queue.length > 0) {
|
||||||
|
playNextInQueue(guildId, voiceChannel);
|
||||||
|
} else {
|
||||||
|
connection.destroy();
|
||||||
|
}
|
||||||
|
fs.unlink(filePath, (err) => {
|
||||||
|
if (err) console.error('Error deleting file:', filePath, err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
audioPlayer.on('error', (err) => {
|
||||||
|
console.error('AudioPlayer error:', err);
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function skipTrack(guildId) {
|
||||||
|
const queue = getQueue(guildId);
|
||||||
|
if (queue.length > 0) {
|
||||||
|
queue.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { addToQueue, getQueue, playNextInQueue, skipTrack };
|
Binary file not shown.
|
@ -0,0 +1,26 @@
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const path = require('path');
|
||||||
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
|
||||||
|
async function downloadVideo(searchQuery) {
|
||||||
|
const tempFilePath = path.join(__dirname, 'tmp', `${uuidv4()}.mp3`);
|
||||||
|
|
||||||
|
const ytDlpArgs = [
|
||||||
|
'--cookies', path.join(__dirname, '../cookies.txt'),
|
||||||
|
'--format', 'bestaudio',
|
||||||
|
'--output', tempFilePath,
|
||||||
|
searchQuery,
|
||||||
|
];
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(`yt-dlp ${ytDlpArgs.join(' ')}`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error('yt-dlp error:', stderr);
|
||||||
|
return reject(new Error('Failed to download video with yt-dlp'));
|
||||||
|
}
|
||||||
|
resolve(tempFilePath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { downloadVideo };
|
Loading…
Reference in New Issue