First push

This commit is contained in:
Wizzard 2024-08-17 11:11:10 -04:00
commit eb75bb1a32
10 changed files with 252 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
cookies.txt
config.json
package-lock.json

63
commands/play.js Normal file
View File

@ -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;
}
}

13
commands/queue.js Normal file
View File

@ -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}`);
}
};

9
commands/skip.js Normal file
View File

@ -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!');
}
};

5
config.json.example Normal file
View File

@ -0,0 +1,5 @@
{
"prefix": "+",
"token": "token",
"cookie-file": "/home/user/dz-musicbot/cookies.txt"
}

53
index.js Normal file
View File

@ -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);

13
package.json Normal file
View File

@ -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"
}
}

66
utils/queueManager.js Normal file
View File

@ -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.

26
utils/yt-dlp.js Normal file
View File

@ -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 };