|
|
|
@ -2,10 +2,10 @@ const cmd = require('./cmd'),
|
|
|
|
|
music = require('./music'),
|
|
|
|
|
utils = require('./utils'),
|
|
|
|
|
config = require('../config.json'),
|
|
|
|
|
sqliteAsync = require('./sqliteAsync'),
|
|
|
|
|
fs = require('fs-extra'),
|
|
|
|
|
servercmd = require('../commands/servercommands'),
|
|
|
|
|
sqlite3 = require('sqlite3'),
|
|
|
|
|
Discord = require('discord.js'),
|
|
|
|
|
handlers = {},
|
|
|
|
|
dataDir = config.dataPath || './data';
|
|
|
|
|
let logger = require('winston');
|
|
|
|
|
|
|
|
|
@ -26,26 +26,16 @@ exports.GuildHandler = class {
|
|
|
|
|
this.mention = false;
|
|
|
|
|
this.prefix = prefix || config.prefix;
|
|
|
|
|
this.servant = new cmd.Servant(this.prefix);
|
|
|
|
|
this.ready = false;
|
|
|
|
|
this.msgsQueue = [];
|
|
|
|
|
// checking if the data direcotry exists and if the gdb directory exists and creates them if they don't
|
|
|
|
|
utils.dirExistence(dataDir, () => {
|
|
|
|
|
utils.dirExistence(dataDir + '/gdb', () => {
|
|
|
|
|
this.db = new sqlite3.Database(`${dataDir}/gdb/${guild}.db`, (err) => {
|
|
|
|
|
if (err)
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
logger.debug(`Connected to the database for ${guild}`);
|
|
|
|
|
this.createTables();
|
|
|
|
|
// register commands
|
|
|
|
|
this.registerMusicCommands();
|
|
|
|
|
this.ready = true;
|
|
|
|
|
// handle all messages that have been received while not being ready
|
|
|
|
|
for (let i = 0; i < this.msgsQueue.length; i++)
|
|
|
|
|
this.handleMessage(this.msgsQueue.shift());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
async initDatabase() {
|
|
|
|
|
await fs.ensureDir(dataDir + '/gdb');
|
|
|
|
|
this.db = new sqliteAsync.Database(`${dataDir}/gdb/${this.guild}.db`);
|
|
|
|
|
await this.db.init();
|
|
|
|
|
logger.debug(`Connected to the database for ${this.guild}`);
|
|
|
|
|
await this.createTables();
|
|
|
|
|
// register commands
|
|
|
|
|
this.registerMusicCommands();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -62,15 +52,15 @@ exports.GuildHandler = class {
|
|
|
|
|
* messages - logs all messages send on the server
|
|
|
|
|
* playlists - save playlists to play them later
|
|
|
|
|
*/
|
|
|
|
|
createTables() {
|
|
|
|
|
this.db.run(`${utils.sql.tableExistCreate} messages (
|
|
|
|
|
async createTables() {
|
|
|
|
|
await this.db.run(`${utils.sql.tableExistCreate} messages (
|
|
|
|
|
${utils.sql.pkIdSerial},
|
|
|
|
|
creation_timestamp DATETIME NOT NULL,
|
|
|
|
|
author VARCHAR(128) NOT NULL,
|
|
|
|
|
author_name VARCHAR(128),
|
|
|
|
|
content TEXT NOT NULL
|
|
|
|
|
)`);
|
|
|
|
|
this.db.run(`${utils.sql.tableExistCreate} playlists (
|
|
|
|
|
await this.db.run(`${utils.sql.tableExistCreate} playlists (
|
|
|
|
|
${utils.sql.pkIdSerial},
|
|
|
|
|
name VARCHAR(32) UNIQUE NOT NULL,
|
|
|
|
|
url VARCHAR(255) NOT NULL
|
|
|
|
@ -88,14 +78,13 @@ exports.GuildHandler = class {
|
|
|
|
|
(this.mention) ? msg.reply('', answer) : msg.channel.send('', answer);
|
|
|
|
|
} else if (answer instanceof Promise) {
|
|
|
|
|
answer
|
|
|
|
|
.then((answer) => this.answerMessage(msg, answer))
|
|
|
|
|
.then((resolvedAnswer) => this.answerMessage(msg, resolvedAnswer))
|
|
|
|
|
.catch((error) => this.answerMessage(msg, error));
|
|
|
|
|
} else {
|
|
|
|
|
(this.mention) ? msg.reply(answer) : msg.channel.send(answer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
logger.debug(`Empty answer won't be send.`);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -103,22 +92,14 @@ exports.GuildHandler = class {
|
|
|
|
|
* replies or just sends the answer.
|
|
|
|
|
* @param msg
|
|
|
|
|
*/
|
|
|
|
|
handleMessage(msg) {
|
|
|
|
|
if (this.ready) {
|
|
|
|
|
if (this.db)
|
|
|
|
|
this.db.run(
|
|
|
|
|
'INSERT INTO messages (author, creation_timestamp, author_name, content) values (?, ?, ?, ?)',
|
|
|
|
|
[msg.author.id, msg.createdTimestamp, msg.author.username, msg.content],
|
|
|
|
|
(err) => {
|
|
|
|
|
if (err)
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.answerMessage(msg, this.servant.parseCommand(msg));
|
|
|
|
|
} else {
|
|
|
|
|
this.msgsQueue.push(msg);
|
|
|
|
|
}
|
|
|
|
|
async handleMessage(msg) {
|
|
|
|
|
if (this.db)
|
|
|
|
|
await this.db.run(
|
|
|
|
|
'INSERT INTO messages (author, creation_timestamp, author_name, content) values (?, ?, ?, ?)',
|
|
|
|
|
[msg.author.id, msg.createdTimestamp, msg.author.username, msg.content]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
this.answerMessage(msg, this.servant.parseCommand(msg));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -127,18 +108,13 @@ exports.GuildHandler = class {
|
|
|
|
|
* @param url
|
|
|
|
|
* @param next
|
|
|
|
|
*/
|
|
|
|
|
connectAndPlay(vc, url, next) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
if (!this.dj.connected) {
|
|
|
|
|
this.dj.connect(vc).then(() => {
|
|
|
|
|
this.dj.playYouTube(url, next);
|
|
|
|
|
resolve();
|
|
|
|
|
}).catch((err) => reject(err));
|
|
|
|
|
} else {
|
|
|
|
|
this.dj.playYouTube(url, next);
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
async connectAndPlay(vc, url, next) {
|
|
|
|
|
if (!this.dj.connected) {
|
|
|
|
|
await this.dj.connect(vc);
|
|
|
|
|
this.dj.playYouTube(url, next);
|
|
|
|
|
} else {
|
|
|
|
|
this.dj.playYouTube(url, next);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -147,82 +123,38 @@ exports.GuildHandler = class {
|
|
|
|
|
registerMusicCommands() {
|
|
|
|
|
this.dj = new music.DJ();
|
|
|
|
|
|
|
|
|
|
// play command
|
|
|
|
|
this.servant.createCommand(servercmd.music.play, (msg, kwargs, argv) => {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let vc = this.dj.voiceChannel || msg.member.voiceChannel;
|
|
|
|
|
let url = kwargs['url'];
|
|
|
|
|
if (!vc)
|
|
|
|
|
reject(servercmd.music.play.response.no_voicechannel);
|
|
|
|
|
if (!url)
|
|
|
|
|
reject(servercmd.music.play.response.no_url);
|
|
|
|
|
if (!utils.YouTube.isValidEntityUrl(url)) {
|
|
|
|
|
if (argv && argv.length > 0)
|
|
|
|
|
url += ' ' + argv.join(' '); // join to get the whole expression behind the command
|
|
|
|
|
this.db.get('SELECT url FROM playlists WHERE name = ?', [url], (err, row) => {
|
|
|
|
|
if (err)
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
if (!row) {
|
|
|
|
|
reject(servercmd.music.play.response.url_invalid);
|
|
|
|
|
logger.verbose('Got invalid url for play command.');
|
|
|
|
|
} else {
|
|
|
|
|
url = row.url;
|
|
|
|
|
|
|
|
|
|
this.connectAndPlay(vc, url).then(() => {
|
|
|
|
|
resolve(servercmd.music.play.response.success);
|
|
|
|
|
}).catch((err) => {
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
reject(servercmd.music.play.response.failure);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
let playCb = async (msg, kwargs, argv, template, next) => {
|
|
|
|
|
let vc = this.dj.voiceChannel || msg.member.voiceChannel;
|
|
|
|
|
let url = kwargs['url'];
|
|
|
|
|
if (!vc)
|
|
|
|
|
return template.response.no_voicechannel;
|
|
|
|
|
if (!url)
|
|
|
|
|
return template.response.no_url;
|
|
|
|
|
if (!utils.YouTube.isValidEntityUrl(url)) {
|
|
|
|
|
if (argv && argv.length > 0)
|
|
|
|
|
url += ' ' + argv.join(' '); // join to get the whole expression behind the command
|
|
|
|
|
let row = await this.db.get('SELECT url FROM playlists WHERE name = ?', [url]);
|
|
|
|
|
if (!row) {
|
|
|
|
|
logger.debug('Got invalid url for play command.');
|
|
|
|
|
return template.response.url_invalid;
|
|
|
|
|
} else {
|
|
|
|
|
this.connectAndPlay(vc, url).then(() => {
|
|
|
|
|
resolve(servercmd.music.play.response.success);
|
|
|
|
|
}).catch((err) => {
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
reject(servercmd.music.play.response.failure);
|
|
|
|
|
});
|
|
|
|
|
await this.connectAndPlay(vc, row.url, next);
|
|
|
|
|
return template.response.success;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
await this.connectAndPlay(vc, url, next);
|
|
|
|
|
return template.response.success;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// play command
|
|
|
|
|
this.servant.createCommand(servercmd.music.play, async (msg, kwargs, argv) => {
|
|
|
|
|
return await playCb(msg, kwargs, argv, servercmd.music.play, false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// playnext command
|
|
|
|
|
this.servant.createCommand(servercmd.music.playnext, (msg, kwargs, argv) => {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let vc = msg.member.voiceChannel;
|
|
|
|
|
if (!this.dj.connected) this.dj.voiceChannel = vc;
|
|
|
|
|
let url = kwargs['url'];
|
|
|
|
|
if (!url) reject(servercmd.music.playnext.response.no_url);
|
|
|
|
|
|
|
|
|
|
if (!utils.YouTube.isValidEntityUrl(url)) {
|
|
|
|
|
if (argv)
|
|
|
|
|
url += ' ' + argv.join(' ');
|
|
|
|
|
this.db.get('SELECT url FROM playlists WHERE name = ?', [url], (err, row) => {
|
|
|
|
|
if (err)
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
if (!row) {
|
|
|
|
|
reject(servercmd.music.play.response.url_invalid);
|
|
|
|
|
} else {
|
|
|
|
|
url = row.url;
|
|
|
|
|
|
|
|
|
|
this.connectAndPlay(vc, url, true).then(() => {
|
|
|
|
|
resolve(servercmd.music.playnext.response.success);
|
|
|
|
|
}).catch((err) => {
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
reject(servercmd.music.play.response.failure);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.connectAndPlay(vc, url, true).then(() => {
|
|
|
|
|
resolve(servercmd.music.playnext.response.success);
|
|
|
|
|
}).catch((err) => {
|
|
|
|
|
logger.error(err);
|
|
|
|
|
reject(servercmd.music.playnext.response.failure);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.servant.createCommand(servercmd.music.playnext, async (msg, kwargs, argv) => {
|
|
|
|
|
return await playCb(msg, kwargs, argv, servercmd.music.playnext, true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// join command
|
|
|
|
@ -326,75 +258,29 @@ exports.GuildHandler = class {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// saves playlists
|
|
|
|
|
this.servant.createCommand(servercmd.music.save, (msg, kwargs, argv) => {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let saveName = argv.join(' ');
|
|
|
|
|
this.db.get('SELECT COUNT(*) count FROM playlists WHERE name = ?', [saveName], (err, row) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
reject();
|
|
|
|
|
}
|
|
|
|
|
let cb = (err) => { // defining the callback for usage below
|
|
|
|
|
if (err)
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
else
|
|
|
|
|
resolve(`Saved song/playlist as ${saveName}`);
|
|
|
|
|
};
|
|
|
|
|
if (!row || row.count === 0)
|
|
|
|
|
this.db.run('INSERT INTO playlists (name, url) VALUES (?, ?)', [saveName, kwargs.url], cb);
|
|
|
|
|
else
|
|
|
|
|
this.db.run('UPDATE playlists SET url = ? WHERE name = ?', [kwargs.url, saveName], cb);
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
this.servant.createCommand(servercmd.music.save, async (msg, kwargs, argv) => {
|
|
|
|
|
let saveName = argv.join(' ');
|
|
|
|
|
let row = await this.db.get('SELECT COUNT(*) count FROM playlists WHERE name = ?', [saveName]);
|
|
|
|
|
if (!row || row.count === 0)
|
|
|
|
|
await this.db.run('INSERT INTO playlists (name, url) VALUES (?, ?)', [saveName, kwargs.url]);
|
|
|
|
|
else
|
|
|
|
|
await this.db.run('UPDATE playlists SET url = ? WHERE name = ?', [kwargs.url, saveName]);
|
|
|
|
|
return `Saved song/playlist as ${saveName}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// saved command - prints out saved playlists
|
|
|
|
|
this.servant.createCommand(servercmd.music.saved, (msg) => {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
let response = '';
|
|
|
|
|
this.db.all('SELECT name, url FROM playlists', (err, rows) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
logger.error(err.message);
|
|
|
|
|
reject();
|
|
|
|
|
}
|
|
|
|
|
for (let row of rows)
|
|
|
|
|
response += `[${row.name}](${row.url})\n`;
|
|
|
|
|
|
|
|
|
|
if (rows.length === 0) {
|
|
|
|
|
msg.channel.send(servercmd.music.saved.response.no_saved);
|
|
|
|
|
} else {
|
|
|
|
|
let richEmbed = new Discord.RichEmbed()
|
|
|
|
|
.setTitle('Saved Songs and Playlists')
|
|
|
|
|
.setDescription(response);
|
|
|
|
|
resolve(richEmbed);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
this.servant.createCommand(servercmd.music.saved, async (msg) => {
|
|
|
|
|
let response = '';
|
|
|
|
|
let rows = await this.db.all('SELECT name, url FROM playlists');
|
|
|
|
|
for (let row of rows)
|
|
|
|
|
response += `[${row.name}](${row.url})\n`;
|
|
|
|
|
|
|
|
|
|
if (rows.length === 0)
|
|
|
|
|
msg.channel.send(servercmd.music.saved.response.no_saved);
|
|
|
|
|
else
|
|
|
|
|
return new Discord.RichEmbed()
|
|
|
|
|
.setTitle('Saved Songs and Playlists')
|
|
|
|
|
.setDescription(response);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param guild
|
|
|
|
|
* @param prefix
|
|
|
|
|
* @returns {GuildHandler}
|
|
|
|
|
* @deprecated use Bot class method instead
|
|
|
|
|
*/
|
|
|
|
|
exports.getHandler = function (guild, prefix) {
|
|
|
|
|
if (!handlers[guild.id])
|
|
|
|
|
handlers[guild.id] = new this.GuildHandler(guild, prefix);
|
|
|
|
|
return handlers[guild.id];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Destroy all handlers to safely end all sql3-clients.
|
|
|
|
|
* @deprecated automated in Bot class cleanup
|
|
|
|
|
*/
|
|
|
|
|
exports.destroyAll = function () {
|
|
|
|
|
logger.debug('Destroying all handlers...');
|
|
|
|
|
for (let key in Object.keys(handlers))
|
|
|
|
|
if (handlers[key])
|
|
|
|
|
handlers[key].destroy();
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|