Merge pull request #8 from Trivernis/develop

Develop
pull/14/head
Trivernis 6 years ago committed by GitHub
commit 6985658d1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

122
bot.js

@ -1,39 +1,72 @@
const Discord = require("discord.js"), const Discord = require("discord.js"),
fs = require('fs'),
logger = require('./lib/logging').getLogger(), logger = require('./lib/logging').getLogger(),
music = require('./lib/music'); music = require('./lib/music'),
cmd = require("./lib/cmd"), cmd = require("./lib/cmd"),
client = new Discord.Client(), client = new Discord.Client(),
args = require('args-parser')(process.argv), args = require('args-parser')(process.argv),
authToken = args.token, authToken = args.token;
prefix = '~';
let savedplaylists = {};
function main() { function main() {
music.setLogger(logger); music.setLogger(logger);
cmd.setLogger(logger); cmd.setLogger(logger);
cmd.init(); cmd.init();
if (fs.existsSync('./data/savedplaylists.json')) {
savedplaylists = JSON.parse(fs.readFileSync('./data/savedplaylists.json'))
}
registerCommands(); registerCommands();
music.setClient(client);
client.login(authToken).then(()=> { client.login(authToken).then(()=> {
logger.debug("Logged in"); logger.debug("Logged in");
}); });
} }
function savePlaylist(url, name) {
savedplaylists[name] = url;
fs.writeFile('./data/savedplaylists.json',JSON.stringify(savedplaylists), (err) => {
if (err) logger.warn(JSON.stringify(err));
})
}
function registerCommands() { function registerCommands() {
cmd.createCommand('~', 'play', (msg, argv) => { cmd.createCommand('~', 'play', (msg, argv) => {
let vc = msg.member.voiceChannel; let vc = msg.member.voiceChannel;
let url = argv['url']; let url = argv['url'];
if (!url) return 'No url given.'; if (!url) return 'No url given.';
if (!url.match(/http/g)) {
if (savedplaylists[url]) {
url = savedplaylists[url];
}
}
try { try {
return music.play(vc, url); return music.play(vc, url);
} catch(err) { } catch(err) {
logger.error(err); logger.error(err);
msg.reply(`${JSON.stringify(err)}`); msg.reply(`${JSON.stringify(err)}`);
} }
}, ['url']); }, ['url'], "Adds the url to the YouTube video/playlist into the queue.");
cmd.createCommand('~', 'playnext', (msg, argv) => {
let vc = msg.member.voiceChannel;
let url = argv['url'];
if (!url) return 'No url given.';
if (!url.match(/http/g)) {
if (savedplaylists[url]) {
url = savedplaylists[url];
}
}
try {
return music.playnext(vc, url);
} catch(err) {
logger.error(err);
msg.reply(`${JSON.stringify(err)}`);
}
}, ['url'], "Plays the YouTube video after the currently playing song.");
cmd.createCommand('~', 'ping', () => { cmd.createCommand('~', 'ping', () => {
return 'Pong!'; return 'Pong!';
}); }, [], "Try it yourself.");
cmd.createCommand('~', 'join', (msg) => { cmd.createCommand('~', 'join', (msg) => {
if (msg.member.voiceChannel) { if (msg.member.voiceChannel) {
@ -42,55 +75,66 @@ function registerCommands() {
else { else {
msg.reply("You are not connected to a voicechannel."); msg.reply("You are not connected to a voicechannel.");
} }
}); }, [], "Joins the VC you are in.");
cmd.createCommand('~', 'stop', (msg) => { cmd.createCommand('~', 'stop', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.stop(vc); music.stop(gid);
}); }, [], "Stops playling music and leavs.");
cmd.createCommand('~', 'pause', (msg) => { cmd.createCommand('~', 'pause', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.pause(vc); music.pause(gid);
}); }, [], "Pauses playing.");
cmd.createCommand('~', 'resume', (msg) => { cmd.createCommand('~', 'resume', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.resume(vc); music.resume(gid);
}); }, [], "Resumes playing.");
cmd.createCommand('~', 'skip', (msg) => { cmd.createCommand('~', 'skip', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.skip(vc); music.skip(gid);
}); }, [], "Skips the current song.");
cmd.createCommand('~', 'plist', (msg) => { cmd.createCommand('~', 'clear', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.getQueue(vc, (songs) => { music.clearQueue(gid);
let songlist = "**Songs**\n"; return "All songs have been deleted, commander :no_mouth: "
}, [],"Clears the playlist.");
cmd.createCommand('~', 'playlist', (msg) => {
let gid = msg.guild.id;
let songs = music.getQueue(gid);
logger.debug(`found ${songs.length} songs`);
let songlist = `**${songs.length} Songs in playlist**\n`;
for (let i = 0; i < songs.length; i++) { for (let i = 0; i < songs.length; i++) {
if (i > 10) break; if (i > 10) break;
songlist += songs[i] + '\n'; songlist += songs[i] + '\n';
} }
msg.reply(songlist); return songlist;
}); }, [], "Shows the next ten songs.");
});
cmd.createCommand('~', 'shuffle', (msg) => { cmd.createCommand('~', 'shuffle', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.shuffle(vc); music.shuffle(gid);
}); return "The queue has successfully been shuffled :slight_smile:"
}, [], "Shuffles the playlist.");
cmd.createCommand('~', 'current', (msg) => { cmd.createCommand('~', 'current', (msg) => {
let vc = msg.member.voiceChannel; let gid = msg.guild.id;
music.nowPlaying(vc, (title, url) => { let song = music.nowPlaying(gid);
msg.reply(`Playing: ${title}\n ${url}`); return `Playing: ${song.title}\n ${song.url}`;
}); }, [], "Shows the currently playing song.");
});
cmd.createCommand('~', 'repeatafterme', (msg, argv) => {
cmd.createCommand('_', 'repeat', (msg, argv) => { return argv['word'];
return argv['repeattext']; }, ['word'], "Repeats a single word you say.");
}, ['repeattext'])
cmd.createCommand('~', 'save', (msg, argv) => {
savePlaylist(argv['url'], argv['name']);
return `Saved song/playlist as ${argv['name']}`
}, ['url', 'name'], "Saves the YouTube song/playlist with a specific name. ~play [name] to play the playlist");
} }
// defining the client's handlers // defining the client's handlers

@ -14,13 +14,14 @@ exports.setLogger = function(newLogger) {
logger = newLogger; logger = newLogger;
}; };
exports.createCommand = function(prefix, command, call, argv) { exports.createCommand = function(prefix, command, call, argv, description) {
try { try {
logger.debug(`Creating command ${command} with prefix ${prefix} and arguments ${argv}`); logger.debug(`Creating command ${command} with prefix ${prefix} and arguments ${argv}`);
if (!commands[prefix]) commands[prefix] = {}; // create Object commands prefix if (!commands[prefix]) commands[prefix] = {}; // create Object commands prefix
commands[prefix][command] = { // assign the command commands[prefix][command] = { // assign the command
args: argv || [], // with arguments args: argv || [],
callback: call // and function callback: call,
description: description
}; };
logger.debug(`Created command ${prefix}${command}`); logger.debug(`Created command ${prefix}${command}`);
} catch (err) { } catch (err) {
@ -80,10 +81,13 @@ exports.init = function() {
helpstr += "Commands\n---\n"; helpstr += "Commands\n---\n";
Object.keys(commands).forEach((key) => { Object.keys(commands).forEach((key) => {
Object.keys(commands[key]).forEach((cmd) => { Object.keys(commands[key]).forEach((cmd) => {
helpstr += "\n" + key + cmd + " " + JSON.stringify(commands[key][cmd].args) + "\n"; helpstr += "\n" + key + cmd + " " + JSON.stringify(commands[key][cmd].args).replace(/"|\[\]/g, '');
if (commands[key][cmd].description) {
helpstr += '\t' + commands[key][cmd].description + '\n';
}
}); });
}); });
helpstr += "```"; helpstr += "```";
return helpstr; return helpstr;
}); }, [], "Shows this help.");
}; };

@ -4,7 +4,7 @@
let logger = require('winston'); let logger = require('winston');
/* Function Definition */ /* Function Definition */
// TODO: Class that handles file-data for a server, functions to get/set data for specific server id
/** /**
* Getting the logger * Getting the logger
* @param {Object} newLogger * @param {Object} newLogger

@ -5,218 +5,363 @@ const Discord = require("discord.js"),
ytapiKey = "AIzaSyBLF20r-c4mXoAT2qBFB5YlCgT0D-izOaU"; ytapiKey = "AIzaSyBLF20r-c4mXoAT2qBFB5YlCgT0D-izOaU";
/* Variable Definition */ /* Variable Definition */
let logger = require('winston'); let logger = require('winston');
let client = null; let djs = {};
let connections = {}; let connections = {};
/* Function Definition */ /* Function Definition */
// TODO: initCommands function that takes the cmd.js module as variable and uses it to create commands // TODO: initCommands function that takes the cmd.js module as variable and uses it to create commands
/** class DJ {
* Getting the logger; constructor(voiceChannel) {
* @param {Object} newLogger this.conn = null;
*/ this.disp = null;
exports.setLogger = function (newLogger) { this.queue = [];
logger = newLogger; this.playing = false;
}; this.current = null;
this.volume = 0.5;
/** this.voiceChannel = voiceChannel;
* Sets the discord Client for the module }
* @param newClient
*/
exports.setClient = function(newClient) {
client = newClient;
};
/** /**
* Connects to a voicechannel * Connects to the given voice channel. Disconnects from the previous one if it exists.
* @param voiceChannel * When the bot was moved and connect is executed again, it connects to the initial VoiceChannel because the
* VoiceChannel is saved as object variable.
* @returns {Promise<T | never>}
*/ */
exports.connect = function(voiceChannel) { connect() {
logger.debug(JSON.stringify()); if (this.conn) {
logger.verbose(`Connecting to voiceChannel ${voiceChannel.name}`); this.stop();
if (client !== null) { }
voiceChannel.join().then(connection => { logger.verbose(`Connecting to voiceChannel ${this.voiceChannel.name}`);
logger.info(`Connected to Voicechannel ${voiceChannel.name}`); return this.voiceChannel.join().then(connection => {
connections[voiceChannel.guild.id] = { logger.info(`Connected to Voicechannel ${this.voiceChannel.name}`);
'conn': connection, this.conn = connection;
'disp': null,
'queue': [],
'playing': false,
current: null
};
}); });
} else {
logger.error("Client is null");
} }
};
/** /**
* Plays a file * Plays a file for the given filename.
* TODO: Implement queue
* @param filename * @param filename
*/ */
exports.playFile = function(voiceChannel, filename) { playFile(filename) {
let gid = voiceChannel.guild.id; if (this.conn !== null) {
let conn = connections[gid].conn; this.disp = this.conn.playFile(filename);
if (conn !== null) { this.playing = true;
connections[gid].disp = conn.playFile(filename);
connections[gid].playing = true;
} else { } else {
this.connect(voiceChannel); logger.warn("Not connected to a voicechannel. Connection now.");
logger.warn("Not connected to a voicechannel"); this.connect(this.voiceChannel).then(() => {
this.playFile(filename);
});
}
} }
};
exports.play = function(voiceChannel, url) { /**
let gid = voiceChannel.guild.id; * Plays the url of the current song if there is no song playing or puts it in the queue.
if (!connections[gid]) this.connect(voiceChannel); * If the url is a playlist (regex match), the videos of the playlist are fetched and put
let conn = connections[gid].conn; * in the queue. For each song the title is saved in the queue too.
if (conn !== null) { * @param url
*/
playYouTube(url, playnext) {
if (!this.conn) this.connect(this.voiceChannel).then(this.playYouTube(url));
let plist = url.match(/(?<=\?list=)[\w\-]+/g); let plist = url.match(/(?<=\?list=)[\w\-]+/g);
if (plist) { if (plist) {
logger.debug(`Adding playlist ${plist} to queue`); logger.debug(`Adding playlist ${plist} to queue`);
ypi(ytapiKey, plist).then(items => { ypi(ytapiKey, plist).then(items => {
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
let vurl = `https://www.youtube.com/watch?v=${items[i].resourceId.videoId}`; let vurl = `https://www.youtube.com/watch?v=${items[i].resourceId.videoId}`;
connections[gid].queue.push(vurl); this.queue.push({'url': vurl, 'title': null});
yttl(vurl.replace(/http(s)?:\/\/(www.)?youtube.com\/watch\?v=/g, ''), (err, title) => {
if (err) {
logger.debug(JSON.stringify(err));
} else {
try {
logger.debug(`Found title: ${title} for ${vurl}`);
this.queue.find((el) => {
return (el.url === vurl);
}).title = title;
} catch (error) {
logger.verbose(JSON.stringify(error));
}
} }
this.play(voiceChannel, connections[gid].queue.shift()); });
}
this.current = this.queue.shift();
this.playYouTube(this.current.url);
}); });
return; return;
} }
if (!connections[gid].playing) { if (!this.playing) {
logger.debug(`Playing ${url}`); logger.debug(`Playing ${url}`);
connections[gid].disp = conn.playStream(ytdl(url, { this.disp = this.conn.playStream(ytdl(url, {
filter: "audioonly" filter: "audioonly"
}), {seek: 0, volume: 0.5}); }), {seek: 0, volume: this.volume});
connections[gid].disp.on('end', () => { this.disp.on('end', () => {
connections[gid].playing = false; this.playing = false;
connections[gid].current = null; this.current = null;
if (connections[gid].queue.length > 0) { if (this.queue.length > 0) {
this.play(voiceChannel, connections[gid].queue.shift()); this.current = this.queue.shift();
this.playYouTube(this.current.url);
} else {
this.stop();
} }
}); });
connections[gid].playing = true; this.playing = true;
connections[gid].current = url;
} else { } else {
logger.debug(`Added ${url} to the queue`); logger.debug(`Added ${url} to the queue`);
connections[gid].queue.push(url); if (playnext) {
this.queue.unshift({'url': url, 'title': null});
} else {
this.queue.push({'url': url, 'title': null});
} }
yttl(url.replace(/http(s)?:\/\/(www.)?youtube.com\/watch\?v=/g, ''), (err, title) => {
if (err) {
logger.debug(JSON.stringify(err));
} else { } else {
logger.warn("Not connected to a voicechannel"); try {
logger.debug(`Found title: ${title} for ${url}`);
this.queue.find((el) => {
return (el.url === url);
}).title = title;
} catch (error) {
console.verbose(JSON.stringify(error));
}
}
});
}
} }
};
/** /**
* Sets the volume of the music * Sets the volume of the dispatcher to the given value
* @param percentage * @param percentage
* @param voiceChannel
*/ */
exports.setVolume = function(voiceChannel, percentage) { setVolume(percentage) {
let disp = connections[voiceChannel.guild.id].disp;
logger.verbose(`Setting volume to ${percentage}`); logger.verbose(`Setting volume to ${percentage}`);
if (disp !== null) { if (this.disp !== null) {
disp.setVolume(percentage); this.volume = percentage;
this.disp.setVolume(percentage);
} else { } else {
logger.warn("No dispatcher found.") logger.warn("No dispatcher found.")
} }
}; }
/** /**
* pauses the music * Pauses if a dispatcher exists
*/ */
exports.pause = function(voiceChannel) { pause() {
let disp = connections[voiceChannel.guild.id].disp;
logger.verbose("Pausing music..."); logger.verbose("Pausing music...");
if (disp !== null) { if (this.disp !== null) {
disp.pause(); this.disp.pause();
} else { } else {
logger.warn("No dispatcher found"); logger.warn("No dispatcher found");
} }
}; }
/** /**
* Resumes the music * Resumes if a dispatcher exists
*/ */
exports.resume = function(voiceChannel) { resume() {
let disp = connections[voiceChannel.guild.id].disp;
logger.verbose("Resuming music..."); logger.verbose("Resuming music...");
if (disp !== null) { if (this.disp !== null) {
disp.resume(); this.disp.resume();
} else { } else {
logger.warn("No dispatcher found"); logger.warn("No dispatcher found");
} }
}; }
/** /**
* Stops the music * Stops playing music by ending the Dispatcher and disconnecting
*/ */
exports.stop = function(voiceChannel) { stop() {
let gid = voiceChannel.guild.id; this.queue = [];
let disp = connections[gid].disp;
let conn = connections[gid].conn;
logger.verbose("Stopping music..."); logger.verbose("Stopping music...");
if (disp !== null) { if (this.disp !== null) {
disp.end(); this.disp.end();
logger.debug("Ended dispatcher"); logger.debug("Ended dispatcher");
} }
if (conn !== null) { if (this.conn !== null) {
conn.disconnect(); this.conn.disconnect();
logger.debug("Ended connection"); logger.debug("Ended connection");
} }
connections[gid].playing = false; }
};
/** /**
* Skips the song * Skips to the next song by ending the current StreamDispatcher and thereby triggering the
* end event of the dispatcher that automatically plays the next song.
*/ */
exports.skip = function(voiceChannel) { skip () {
let disp = connections[voiceChannel.guild.id].disp;
logger.debug("Skipping song"); logger.debug("Skipping song");
if (disp !== null) { if (this.disp !== null) {
disp.end(); this.disp.end();
}
} }
};
/** /**
* executes the callback when the titlelist is finished * Returns the title for each song saved in the queue
* @returns {Array}
*/ */
exports.getQueue = function(voiceChannel, callback) { get playlist() {
let titles = []; let songs = [];
connections[voiceChannel.guild.id].queue.forEach((url) => { this.queue.forEach((entry) => {
yttl(url.replace(/http(s)?:\/\/(www.)?youtube.com\/watch\?v=/g, ''), (err, title) => { songs.push(entry.title);
if (err) {
logger.error(err);
} else {
titles.push(title);
}
}); });
}); return songs;
setTimeout(() => callback(titles), 2000 ); }
/**
* Returns the song saved in the private variable 'current'
* @returns {null|*}
*/
get song() {
return this.current;
}
/**
* Shuffles the queue
*/
shuffle() {
this.queue = shuffleArray(this.queue);
}
/**
* Clears the playlist
*/
clear() {
this.queue = [];
}
}
/**
* Getting the logger;
* @param {Object} newLogger
*/
exports.setLogger = function (newLogger) {
logger = newLogger;
}; };
/** /**
* evokes the callback function with the title of the current song * Connects to a voicechannel
* @param callback
* @param voiceChannel * @param voiceChannel
*/ */
exports.nowPlaying = function(voiceChannel, callback) { exports.connect = function(voiceChannel) {
let gid = voiceChannel.guild.id; let gid = voiceChannel.guild.id;
if (connections[gid].queue.length > 0) { let voiceDJ = new DJ(voiceChannel);
yttl(connections[gid].current.replace(/http(s)?:\/\/(www.)?youtube.com\/watch\?v=/g, ''), (err, title) => { djs[gid] = voiceDJ;
if (err) { return voiceDJ.connect();
logger.error(err); };
/**
* Plays a file
* @param filename
* @param guildId
*/
exports.playFile = function(guildId, filename) {
djs[guildId].playFile(filename);
};
/**
* Plays a YT Url
* @param voiceChannel
* @param url
*/
exports.play = function(voiceChannel, url) {
let guildId = voiceChannel.guild.id;
if (!djs[guildId]) {
this.connect(voiceChannel).then(() => {
djs[guildId].playYouTube(url);
});
} else { } else {
callback(title, connections[gid].current); djs[guildId].playYouTube(url);
} }
};
/**
* plays the given url as next song
* @param voiceChannel
* @param url
*/
exports.playnext = function(voiceChannel, url) {
let guildId = voiceChannel.guild.id;
if (!djs[guildId]) {
this.connect(voiceChannel).then(() => {
djs[guildId].playYouTube(url, true);
}); });
} else {
djs[guildId].playYouTube(url, true);
} }
}; };
/**
* Sets the volume of the music
* @param percentage
* @param guildId
*/
exports.setVolume = function(guildId, percentage) {
djs[guildId].setVolume(percentage);
};
/**
* pauses the music
*/
exports.pause = function(guildId) {
djs[guildId].pause();
};
/**
* Resumes the music
* @param guildId
*/
exports.resume = function(guildId) {
djs[guildId].resume();
};
/**
* Stops the music
* @param guildId
*/
exports.stop = function(guildId) {
djs[guildId].stop();
delete djs[guildId];
};
/**
* Skips the song
* @param guildId
*/
exports.skip = function(guildId) {
djs[guildId].skip();
};
/**
* Clears the playlist
* @param guildId
*/
exports.clearQueue = function(guildId) {
djs[guildId].clear();
};
/**
* Returns the queue
* @param guildId
*/
exports.getQueue = function(guildId) {
return djs[guildId].playlist;
};
/**
* evokes the callback function with the title of the current song
* @param guildId
*/
exports.nowPlaying = function(guildId) {
return djs[guildId].song;
};
/** /**
* shuffles the queue * shuffles the queue
* @param guildId
*/ */
exports.shuffle = function(voiceChannel) { exports.shuffle = function(guildId) {
connections[voiceChannel.guild.id].queue = shuffle(connections[voiceChannel.guild.id].queue); djs[guildId].shuffle();
}; };
/** /**
@ -224,7 +369,7 @@ exports.shuffle = function(voiceChannel) {
* @param array * @param array
* @returns {Array} * @returns {Array}
*/ */
function shuffle(array) { function shuffleArray(array) {
let currentIndex = array.length, temporaryValue, randomIndex; let currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle... // While there remain elements to shuffle...

Loading…
Cancel
Save