You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
12 KiB
JavaScript
326 lines
12 KiB
JavaScript
/* Module definition */
|
|
|
|
/* Variable Definition */
|
|
const Discord = require('discord.js'),
|
|
args = require('args-parser')(process.argv),
|
|
config = require('../config.json'),
|
|
gcmdTempl = require('../commands/globalcommands'),
|
|
scmdTempl = require('../commands/servercommands');
|
|
|
|
let logger = require('winston'),
|
|
globCommands = {};
|
|
|
|
/**
|
|
* @type {Servant}
|
|
*/
|
|
exports.Servant = class {
|
|
constructor(prefix) {
|
|
this.commands = {};
|
|
this.prefix = prefix;
|
|
// show all commands (except the owner commands if the user is not an owner)
|
|
this.createCommand(gcmdTempl.utils.help, (msg, kwargs) => {
|
|
if (kwargs.command) {
|
|
let cmd = kwargs.command;
|
|
let allCommands = {...globCommands, ...this.commands};
|
|
if (cmd.charAt(0) !== prefix)
|
|
cmd = this.prefix + cmd;
|
|
if (allCommands[cmd])
|
|
return new Discord.RichEmbed()
|
|
.setTitle(`Help for ${cmd}`)
|
|
.addField('Usage', `\`${cmd} [${allCommands[cmd].args.join('] [')}]\``.replace('[]', ''))
|
|
.addField('Description', allCommands[cmd].description)
|
|
.addField('Permission Role', allCommands[cmd].role || 'all');
|
|
else
|
|
return 'Command not found :(';
|
|
|
|
} else {
|
|
let helpEmbed = new Discord.RichEmbed()
|
|
.setTitle('Commands')
|
|
.setDescription('Create a sequence of commands with `;` (semicolon).')
|
|
.setTimestamp();
|
|
let globHelp = '';
|
|
Object.entries(globCommands).sort().forEach(([key, value]) => {
|
|
if (value.role !== 'owner' || checkPermission(msg, 'owner'))
|
|
globHelp += `\`${key}\` \t`;
|
|
|
|
});
|
|
helpEmbed.addField('Global Commands', globHelp);
|
|
let categories = [];
|
|
let catCommands = {};
|
|
Object.entries(this.commands).sort().forEach(([key, value]) => {
|
|
if (value.role !== 'owner' || checkPermission(msg, 'owner'))
|
|
if (!categories.includes(value.category)) {
|
|
categories.push(value.category);
|
|
catCommands[value.category] = `\`${key}\` \t`;
|
|
} else {
|
|
catCommands[value.category] += `\`${key}\` \t`;
|
|
}
|
|
|
|
});
|
|
for (let cat of categories)
|
|
helpEmbed.addField(cat, catCommands[cat]);
|
|
|
|
helpEmbed.setFooter(prefix + 'help [command] for more info to each command');
|
|
return helpEmbed;
|
|
}
|
|
});
|
|
|
|
// show all roles that are used by commands
|
|
this.createCommand(scmdTempl.utils.roles, () => {
|
|
let roles = [];
|
|
Object.values(globCommands).concat(Object.values(this.commands)).sort().forEach((value) => {
|
|
roles.push(value.role || 'all');
|
|
});
|
|
return `**Roles**\n${[...new Set(roles)].join('\n')}`;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Creates a command entry in the private commands dict
|
|
* @param template
|
|
* @param call
|
|
*/
|
|
createCommand(template, call) {
|
|
if (!template.name) {
|
|
logger.debug(`Name of command template is null or undef. Failed creating command.`);
|
|
return;
|
|
}
|
|
this.commands[this.prefix + template.name] = {
|
|
'args': template.args || [],
|
|
'description': template.description,
|
|
'callback': call,
|
|
'role': template.permission,
|
|
'category': template.category || 'Other'
|
|
};
|
|
logger.debug(`Created server command: ${this.prefix + template.name}, args: ${template.args}`);
|
|
}
|
|
|
|
/**
|
|
* Removes a command
|
|
* @param command
|
|
* @deprecated Why would you want to remove a command?
|
|
*/
|
|
removeCommand(command) {
|
|
delete this.commands[command];
|
|
}
|
|
|
|
/**
|
|
* Processes the command
|
|
* @param msg
|
|
* @param globResult
|
|
* @param content
|
|
* @param returnFunction Boolean if the return value should be a function.
|
|
* @param fallback
|
|
* @returns {*}
|
|
*/
|
|
processCommand(msg, globResult, content, returnFunction, fallback) {
|
|
let command = (content.match(/^.\w+/) || [])[0];
|
|
if (!command || !this.commands[command])
|
|
if (fallback && !globResult) {
|
|
command = fallback;
|
|
content = `${fallback} ${content}`;
|
|
} else {
|
|
return globResult;
|
|
}
|
|
let cmd = this.commands[command];
|
|
if (!checkPermission(msg, cmd.role))
|
|
return 'No Permission';
|
|
logger.debug(`Permission <${cmd.role || 'all'}> granted for command ${command} for user <${msg.author.tag}>`);
|
|
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: ${command}, kwargs: ${kwargs}, argv: ${argv}`);
|
|
try {
|
|
let locResult = returnFunction? () => cmd.callback(msg, kwargs, argv) : cmd.callback(msg, kwargs, argv);
|
|
return locResult || globResult;
|
|
} catch (err) {
|
|
logger.error(err.message);
|
|
return `The command \`${command}\` has thrown an error.`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 if (commands.length < (config.maxCmdSequenceLength || 5)) {
|
|
let answers = [];
|
|
let previousCommand = (commands[0].match(/^.\w+/) || [])[0];;
|
|
for (let i = 0; i < commands.length; i++) {
|
|
answers.push(this.processCommand(msg, globResult[i], commands[i],
|
|
true, previousCommand)); // return function to avoid "race conditions"
|
|
let commandMatch = (commands[i].match(/^.\w+/) || [])[0];
|
|
previousCommand = this.commands[commandMatch]? commandMatch : previousCommand;
|
|
}
|
|
|
|
return answers;
|
|
} else {
|
|
return 'This command sequence is too long!';
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Getting the logger
|
|
* @param {Object} newLogger
|
|
*/
|
|
exports.setLogger = function (newLogger) {
|
|
logger = newLogger;
|
|
};
|
|
|
|
/**
|
|
* Creates a global command that can be executed in every channel.
|
|
* @param prefix
|
|
* @param template
|
|
* @param call
|
|
*/
|
|
exports.createGlobalCommand = function (prefix, template, call) {
|
|
if (!template.name) {
|
|
logger.debug(`Name of command template is null or undef. Failed to create command.`);
|
|
return;
|
|
}
|
|
globCommands[prefix + template.name] = {
|
|
'args': template.args || [],
|
|
'description': template.description,
|
|
'callback': call,
|
|
'role': template.permission,
|
|
'name': template.name,
|
|
'category': template.category || 'Other'
|
|
};
|
|
logger.debug(`Created global command: ${prefix + template.name}, args: ${template.args}`);
|
|
};
|
|
|
|
|
|
/**
|
|
* Parses a message for a global command
|
|
* @param msg
|
|
* @returns {boolean|*}
|
|
*/
|
|
exports.parseMessage = function (msg) {
|
|
return parseGlobalCommand(msg);
|
|
};
|
|
|
|
/**
|
|
* Initializes the module by creating a help command
|
|
*/
|
|
exports.init = function (prefix) {
|
|
logger.verbose("Created help command");
|
|
this.createGlobalCommand((prefix || config.prefix), gcmdTempl.utils.help, (msg, kwargs) => {
|
|
if (kwargs.command) {
|
|
let cmd = kwargs.command;
|
|
if (cmd.charAt(0) !== prefix)
|
|
cmd = prefix + cmd;
|
|
if (globCommands[cmd])
|
|
return new Discord.RichEmbed()
|
|
.setTitle(`Help for ${cmd}`)
|
|
.addField('Usage', `\`${cmd} [${globCommands[cmd].args.join('] [')}]\``.replace('[]', ''))
|
|
.addField('Description', globCommands[cmd].description)
|
|
.addField('Permission Role', globCommands[cmd].role || 'all');
|
|
|
|
} else {
|
|
let helpEmbed = new Discord.RichEmbed()
|
|
.setTitle('Global Commands')
|
|
.setDescription('Create a sequence of commands with `;` (semicolon).')
|
|
.setTimestamp();
|
|
let description = '';
|
|
Object.entries(globCommands).sort().forEach(([key, value]) => {
|
|
if (value.role === 'owner' && checkPermission(msg, 'owner'))
|
|
description += `\`${key}\` \t`;
|
|
else if (value.role !== 'owner')
|
|
description += `\`${key}\` \t`;
|
|
|
|
});
|
|
helpEmbed.setFooter(prefix + 'help [command] for more info to each command');
|
|
helpEmbed.setDescription(description);
|
|
return helpEmbed;
|
|
}
|
|
});
|
|
};
|
|
|
|
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
|
|
* @param msg
|
|
*/
|
|
function parseGlobalCommand(msg) {
|
|
let content = msg.content;
|
|
let commands = content.split(/(?<!\\);/).map(x => x.replace(/^ +/, ''));
|
|
if (commands.length === 1) {
|
|
let command = (content.match(/^.\w+/) || [])[0];
|
|
if (!command || !globCommands[command])
|
|
return false;
|
|
let cmd = globCommands[command];
|
|
if (!checkPermission(msg, cmd.role))
|
|
return false;
|
|
logger.debug(`Permission <${cmd.role}> granted for command ${command} for user <${msg.author.tag}>`);
|
|
return processCommand(cmd, msg, content);
|
|
} else if (commands.length < (config.maxCmdSequenceLength || 5)) {
|
|
let answers = [];
|
|
let previousCommand = '';
|
|
for (let commandPart of commands) {
|
|
let command = (commandPart.match(/^.\w+/) || [])[0] || previousCommand;
|
|
previousCommand = globCommands[command]? command : previousCommand;
|
|
if (!commandPart || !globCommands[command]) {
|
|
commandPart = `${previousCommand} ${commandPart}`;
|
|
command = previousCommand;
|
|
}
|
|
if (command && globCommands[command]) {
|
|
let cmd = globCommands[command];
|
|
if (checkPermission(msg, cmd.role)) {
|
|
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"
|
|
} else {
|
|
answers.push(false);
|
|
}
|
|
} else {
|
|
answers.push(false);
|
|
}
|
|
}
|
|
return answers;
|
|
} else {
|
|
return 'This command sequence is too long!';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param msg
|
|
* @param rolePerm {String}
|
|
* @returns {boolean}
|
|
*/
|
|
function checkPermission(msg, rolePerm) {
|
|
if (!rolePerm || ['all', 'any', 'everyone'].includes(rolePerm))
|
|
return true;
|
|
if (msg.author.tag === args.owner || config.owners.includes(msg.author.tag))
|
|
return true;
|
|
else
|
|
if (msg.member && rolePerm && rolePerm !== 'owner' && msg.member.roles
|
|
.some(role => (role.name.toLowerCase() === rolePerm.toLowerCase() || role.name.toLowerCase() === 'botcommander')))
|
|
return true;
|
|
|
|
return false;
|
|
}
|