Added command listing

- improved asynchronous command feature
- added waterfall to resolve array answers
- added function and array type to answerMessage types
pull/36/head
Trivernis 6 years ago
parent 13493d8db5
commit dd2a49209f

@ -6,6 +6,7 @@ const Discord = require("discord.js"),
utils = require('./lib/utils'), utils = require('./lib/utils'),
config = require('./config.json'), config = require('./config.json'),
args = require('args-parser')(process.argv), args = require('args-parser')(process.argv),
waterfall = require('promise-waterfall'),
sqliteAsync = require('./lib/sqliteAsync'), sqliteAsync = require('./lib/sqliteAsync'),
authToken = args.token || config.api.botToken, authToken = args.token || config.api.botToken,
prefix = args.prefix || config.prefix || '~', prefix = args.prefix || config.prefix || '~',
@ -177,7 +178,7 @@ class Bot {
try { try {
await msg.reply('Shutting down...'); await msg.reply('Shutting down...');
logger.debug('Destroying client...'); logger.debug('Destroying client...');
} catch(err) { } catch (err) {
logger.error(err.message); logger.error(err.message);
logger.debug(err.stack); logger.debug(err.stack);
} }
@ -192,7 +193,7 @@ class Bot {
await this.webServer.stop(); await this.webServer.stop();
logger.debug(`Exiting Process...`); logger.debug(`Exiting Process...`);
process.exit(0); process.exit(0);
} catch(err) { } catch (err) {
logger.error(err.message); logger.error(err.message);
logger.debug(err.stack); logger.debug(err.stack);
} }
@ -325,20 +326,22 @@ class Bot {
* @param msg * @param msg
* @param answer * @param answer
*/ */
answerMessage(msg, answer) { async answerMessage(msg, answer) {
if (answer instanceof Promise || answer)
if (answer instanceof Discord.RichEmbed) { if (answer instanceof Discord.RichEmbed) {
(this.mention) ? msg.reply('', answer) : msg.channel.send('', answer); (this.mention) ? msg.reply('', answer) : msg.channel.send('', answer);
} else if (answer instanceof Promise) { } else if (answer instanceof Promise) {
answer let resolvedAnswer = await answer;
.then((answer) => this.answerMessage(msg, answer)) await this.answerMessage(msg, resolvedAnswer);
.catch((error) => this.answerMessage(msg, error)); } else if (answer instanceof Array) {
} else { let answerPromises = [];
for (let answerPart of answer)
answerPromises.push(async () => await this.answerMessage(msg, answerPart));
await waterfall(answerPromises);
} else if ({}.toString.call(answer) === '[object Function]') {
await this.answerMessage(msg, answer());
} else if (answer) {
(this.mention) ? msg.reply(answer) : msg.channel.send(answer); (this.mention) ? msg.reply(answer) : msg.channel.send(answer);
} }
else
logger.verbose(`Empty answer won't be send.`);
} }
/** /**

@ -103,18 +103,19 @@ exports.Servant = class {
} }
/** /**
* Parses the message and executes the command callback for the found command entry in the commands dict * Processes the command
* @param msg * @param msg
* @param globResult
* @param content
* @returns {*} * @returns {*}
*/ */
parseCommand(msg) { processCommand(msg, globResult, content, returnFunction) {
let globResult = parseGlobalCommand(msg);
logger.debug(`Global command result is ${globResult}`);
let content = msg.content;
let command = (content.match(/^.\w+/) || [])[0]; let command = (content.match(/^.\w+/) || [])[0];
if (!command || !this.commands[command]) return globResult; if (!command || !this.commands[command])
return globResult;
let cmd = this.commands[command]; let cmd = this.commands[command];
if (!checkPermission(msg, cmd.role)) return 'No Permission'; if (!checkPermission(msg, cmd.role))
return 'No Permission';
logger.debug(`Permission <${cmd.role || 'all'}> granted for command ${command} for user <${msg.author.tag}>`); logger.debug(`Permission <${cmd.role || 'all'}> granted for command ${command} for user <${msg.author.tag}>`);
let argvars = content.match(/(?<= )\S+/g) || []; let argvars = content.match(/(?<= )\S+/g) || [];
let kwargs = {}; let kwargs = {};
@ -125,9 +126,7 @@ exports.Servant = class {
let argv = argvars.slice(nLength); let argv = argvars.slice(nLength);
logger.debug(`Executing callback for command: ${command}, kwargs: ${kwargs}, argv: ${argv}`); logger.debug(`Executing callback for command: ${command}, kwargs: ${kwargs}, argv: ${argv}`);
try { try {
let locResult = cmd.callback(msg, kwargs, argv); let locResult = returnFunction? () => cmd.callback(msg, kwargs, argv) : cmd.callback(msg, kwargs, argv);
if (locResult instanceof Promise)
return locResult; // because Promise equals false in conditional
return locResult || globResult; return locResult || globResult;
} catch (err) { } catch (err) {
logger.error(err.message); logger.error(err.message);
@ -135,6 +134,28 @@ exports.Servant = class {
} }
} }
/**
* Parses the message and executes the command callback for the found command entry in the commands dict
* @param msg
* @returns {*}
*/
parseCommand(msg) {
let globResult = parseGlobalCommand(msg);
logger.debug(`Global command result is ${globResult}`);
let content = msg.content;
let commands = content.split(';').map(x => x.replace(/^ +/, ''));
if (commands.length === 1) {
return this.processCommand(msg, globResult, content);
} else {
let answers = [];
for (let i = 0; i < commands.length; i++)
answers.push(this.processCommand(msg, globResult[i], commands[i],
true)); // return function to avoid "race conditions"
return answers;
}
}
}; };
/** /**
@ -158,7 +179,8 @@ exports.createGlobalCommand = function (command, call, args, description, role)
'args': args || [], 'args': args || [],
'description': description, 'description': description,
'callback': call, 'callback': call,
'role': role 'role': role,
'name': command
}; };
logger.debug(`Created global command: ${command}, args: ${args}`); logger.debug(`Created global command: ${command}, args: ${args}`);
}; };
@ -209,26 +231,48 @@ exports.init = function (prefix) {
}, ['command'], "Shows this help."); }, ['command'], "Shows this help.");
}; };
function processCommand(cmd, msg, content, returnFunction) {
let argvars = content.match(/(?<= )\S+/g) || [];
let kwargs = {};
let nLength = Math.min(cmd.args.length, argvars.length);
for (let i = 0; i < nLength; i++)
kwargs[cmd.args[i]] = argvars[i];
let argv = argvars.slice(nLength);
logger.debug(`Executing callback for command: ${cmd.name}, kwargs: ${JSON.stringify(kwargs)}, argv: ${argv}`);
return returnFunction? () => cmd.callback(msg, kwargs, argv) : cmd.callback(msg, kwargs, argv);
}
/** /**
* Parses the message by calling the assigned function for the command with arguments * Parses the message by calling the assigned function for the command with arguments
* @param msg * @param msg
*/ */
function parseGlobalCommand(msg) { function parseGlobalCommand(msg) {
let content = msg.content; let content = msg.content;
let commands = content.split(';').map(x => x.replace(/^ +/, ''));
if (commands.length === 1) {
let command = (content.match(/^.\w+/) || [])[0]; let command = (content.match(/^.\w+/) || [])[0];
if (!command || !globCommands[command]) return false; if (!command || !globCommands[command])
return false;
let cmd = globCommands[command]; let cmd = globCommands[command];
if (!checkPermission(msg, cmd.role)) return false; if (!checkPermission(msg, cmd.role))
return false;
logger.debug(`Permission <${cmd.role}> granted for command ${command} for user <${msg.author.tag}>`); logger.debug(`Permission <${cmd.role}> granted for command ${command} for user <${msg.author.tag}>`);
let argvars = content.match(/(?<= )\S+/g) || []; return processCommand(cmd, msg, content);
let kwargs = {}; } else {
let nLength = Math.min(cmd.args.length, argvars.length); let answers = [];
for (let i = 0; i < nLength; i++) for (let commandPart of commands) {
kwargs[cmd.args[i]] = argvars[i]; let command = (commandPart.match(/^.\w+/) || [])[0];
if (command && globCommands[command]) {
let argv = argvars.slice(nLength); let cmd = globCommands[command];
logger.debug(`Executing callback for command: ${command}, kwargs: ${JSON.stringify(kwargs)}, argv: ${argv}`); if (checkPermission(msg, cmd.role)) {
return cmd.callback(msg, kwargs, argv); logger.debug(`Permission <${cmd.role}> granted for command ${command} for user <${msg.author.tag}>`);
answers.push(processCommand(cmd, msg, commandPart,
true)); // return an function to avoid "race conditions"
}
}
}
return answers;
}
} }
/** /**

@ -6,6 +6,7 @@ const cmd = require('./cmd'),
fs = require('fs-extra'), fs = require('fs-extra'),
servercmd = require('../commands/servercommands'), servercmd = require('../commands/servercommands'),
Discord = require('discord.js'), Discord = require('discord.js'),
waterfall = require('promise-waterfall'),
dataDir = config.dataPath || './data'; dataDir = config.dataPath || './data';
let logger = require('winston'); let logger = require('winston');
@ -72,19 +73,23 @@ exports.GuildHandler = class {
* @param msg * @param msg
* @param answer * @param answer
*/ */
answerMessage(msg, answer) { async answerMessage(msg, answer) {
if (answer instanceof Promise || answer) if (answer instanceof Promise || answer)
if (answer instanceof Discord.RichEmbed) { if (answer instanceof Discord.RichEmbed) {
(this.mention) ? msg.reply('', answer) : msg.channel.send('', answer); (this.mention) ? msg.reply('', answer) : msg.channel.send('', answer);
} else if (answer instanceof Promise) { } else if (answer instanceof Promise) {
answer let resolvedAnswer = await answer;
.then((resolvedAnswer) => this.answerMessage(msg, resolvedAnswer)) await this.answerMessage(msg, resolvedAnswer);
.catch((error) => this.answerMessage(msg, error)); } else if (answer instanceof Array) {
let answerPromises = [];
for (let answerPart of answer)
answerPromises.push(async () => await this.answerMessage(msg, answerPart));
await waterfall(answerPromises);
} else if ({}.toString.call(answer) === '[object Function]') { // check if the answer is of type function
await this.answerMessage(msg, answer());
} else { } else {
(this.mention) ? msg.reply(answer) : msg.channel.send(answer); (this.mention) ? msg.reply(answer) : msg.channel.send(answer);
} }
else
logger.debug(`Empty answer won't be send.`);
} }
/** /**
@ -98,8 +103,7 @@ exports.GuildHandler = class {
'INSERT INTO messages (author, creation_timestamp, author_name, content) values (?, ?, ?, ?)', 'INSERT INTO messages (author, creation_timestamp, author_name, content) values (?, ?, ?, ?)',
[msg.author.id, msg.createdTimestamp, msg.author.username, msg.content] [msg.author.id, msg.createdTimestamp, msg.author.username, msg.content]
); );
await this.answerMessage(msg, this.servant.parseCommand(msg));
this.answerMessage(msg, this.servant.parseCommand(msg));
} }
/** /**
@ -111,9 +115,9 @@ exports.GuildHandler = class {
async connectAndPlay(vc, url, next) { async connectAndPlay(vc, url, next) {
if (!this.dj.connected) { if (!this.dj.connected) {
await this.dj.connect(vc); await this.dj.connect(vc);
this.dj.playYouTube(url, next); await this.dj.playYouTube(url, next);
} else { } else {
this.dj.playYouTube(url, next); await this.dj.playYouTube(url, next);
} }
} }

@ -34,10 +34,15 @@ exports.DJ = class {
* VoiceChannel is saved as object variable. * VoiceChannel is saved as object variable.
*/ */
async connect(voiceChannel) { async connect(voiceChannel) {
this.voiceChannel = voiceChannel || this.voiceChannel;
if (this.connected) if (this.connected)
if (voiceChannel && this.voiceChannel !== voiceChannel) {
this.voiceChannel = voiceChannel;
this.stop(); this.stop();
} else {
return;
}
else
this.voiceChannel = voiceChannel;
logger.verbose(`Connecting to voiceChannel ${this.voiceChannel.name}`); logger.verbose(`Connecting to voiceChannel ${this.voiceChannel.name}`);
let connection = await this.voiceChannel.join(); let connection = await this.voiceChannel.join();
logger.info(`Connected to Voicechannel ${this.voiceChannel.name}`); logger.info(`Connected to Voicechannel ${this.voiceChannel.name}`);
@ -72,9 +77,11 @@ exports.DJ = class {
* @param voiceChannel * @param voiceChannel
*/ */
updateChannel(voiceChannel) { updateChannel(voiceChannel) {
if (voiceChannel) {
this.voiceChannel = voiceChannel; this.voiceChannel = voiceChannel;
logger.debug(`Updated voiceChannel to ${this.voiceChannel.name}`); logger.debug(`Updated voiceChannel to ${this.voiceChannel.name}`);
} }
}
/** /**
* Plays a file for the given filename. * Plays a file for the given filename.
@ -258,7 +265,6 @@ exports.DJ = class {
logger.debug("Ended dispatcher"); logger.debug("Ended dispatcher");
} }
if (this.conn) { if (this.conn) {
this.conn.channel.leave();
this.conn.disconnect(); this.conn.disconnect();
this.conn = null; this.conn = null;
logger.debug("Ended connection"); logger.debug("Ended connection");

@ -25,6 +25,7 @@
"js-sha512": "0.8.0", "js-sha512": "0.8.0",
"node-sass": "4.11.0", "node-sass": "4.11.0",
"opusscript": "0.0.6", "opusscript": "0.0.6",
"promise-waterfall": "^0.1.0",
"pug": "2.0.3", "pug": "2.0.3",
"sqlite3": "4.0.6", "sqlite3": "4.0.6",
"winston": "3.2.1", "winston": "3.2.1",

Loading…
Cancel
Save