You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
6.5 KiB
217 lines
6.5 KiB
/**
|
|
* Base module that all modules extend
|
|
*/
|
|
|
|
let { logger } = require('../logging');
|
|
let message = require('../message');
|
|
let { isFunction, getObjectKeysToPrototype } = require('../utility');
|
|
let { getConfig, sanitizeConfig } = require('../config');
|
|
|
|
class AbstractModule {
|
|
/*
|
|
Name of the module used in help documentation and logging.
|
|
*/
|
|
name = "AbstractModule";
|
|
|
|
/*
|
|
Short description of the module functionality.
|
|
*/
|
|
description = "Base Module That All Other Modules Extend";
|
|
|
|
/*
|
|
A helpful multiline string that defines the module usage
|
|
*/
|
|
helpAndUsage = `Example: !abstract_module <command>
|
|
!abstract_module <boo> : scares people`
|
|
|
|
/*
|
|
The exported command used to invoke the module directly.
|
|
*/
|
|
command = "abstract_module";
|
|
|
|
/*
|
|
The default method to call when a command word is not recognized.
|
|
*/
|
|
defaultCommand = null;
|
|
|
|
/*
|
|
The module should be hidden from help and command dialogs.
|
|
*/
|
|
hidden = false;
|
|
|
|
/*
|
|
This module should receive all messages, regardless of whether
|
|
the module was directly referenced with a command.
|
|
*/
|
|
canHandleIndirectMessages = false;
|
|
|
|
/*
|
|
Indicates if the modules needs access to the global config
|
|
*/
|
|
needGlobalConfig = false;
|
|
|
|
/*
|
|
Indicates if the module requires a readable config file.
|
|
*/
|
|
needConfig = false;
|
|
|
|
/* internal */
|
|
|
|
/*
|
|
The global config passed in
|
|
*/
|
|
_global_config = null;
|
|
|
|
/*
|
|
The loaded config file, if it exists.
|
|
*/
|
|
_config = null;
|
|
|
|
constructor(name, description, command) {
|
|
this.name = name;
|
|
this.description = description;
|
|
this.command = command;
|
|
this._recognizedCommands = [];
|
|
this._recognizedCommandMap = new Map();
|
|
}
|
|
|
|
addRecognizedCommand(command, methodName) {
|
|
this._recognizedCommands.push(command);
|
|
this._recognizedCommandMap.set(command, methodName);
|
|
}
|
|
|
|
getRecognizedCommands() {
|
|
return this._recognizedCommandMap.keys();
|
|
}
|
|
|
|
getConfigFilePath() {
|
|
return process.env.NODE_PATH + '/data/' + this.name.toLowerCase().replace(' ', '_') + '-config.json';
|
|
}
|
|
|
|
getConfigSensitiveFields() {
|
|
return [];
|
|
}
|
|
|
|
getGlobal(key, defaultValue = null) {
|
|
if (this._global_config !== null && typeof this._global_config[key] !== 'undefined') {
|
|
return this._global_config[key];
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
get(key, defaultValue = null) {
|
|
if (this._config !== null && typeof this._config[key] !== 'undefined') {
|
|
return this._config[key];
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
/**
|
|
* Default functionality for receiving and processing a message.
|
|
*
|
|
* Override this if the module needs to do more complicated message processing.
|
|
*/
|
|
handleMessage(event, room, callback) {
|
|
logger.debug("[%s] [%s] [%s]", this.name, room.name, event.event.content.body);
|
|
|
|
let messageBody = event.event.content.body;
|
|
let bodyParts = messageBody.split(' ');
|
|
let trigger = bodyParts[0];
|
|
let command = bodyParts[1];
|
|
var args = [];
|
|
if (bodyParts.length > 2) {
|
|
args = bodyParts.slice(2);
|
|
}
|
|
|
|
logger.debug("Attempting to call %s with %s", command, args);
|
|
let responseMessage = this.processMessage(command, ...args);
|
|
|
|
callback(
|
|
room,
|
|
responseMessage
|
|
);
|
|
}
|
|
|
|
/*
|
|
Call the command method with the args
|
|
*/
|
|
processMessage(command, ...args) {
|
|
if (this._recognizedCommands.includes(command)) {
|
|
logger.debug("Calling %s with %s", this._recognizedCommandMap.get(command), args);
|
|
return this[this._recognizedCommandMap.get(command)](...args);
|
|
} else {
|
|
if (this.defaultCommand != null) {
|
|
logger.debug("Attempting to use default command %s", this.defaultCommand);
|
|
try {
|
|
let newArgs = [command].concat(...args);
|
|
logger.debug("Calling %s with %s", this._recognizedCommandMap.get(this.defaultCommand), newArgs);
|
|
return this[this._recognizedCommandMap.get(this.defaultCommand)](...newArgs);
|
|
} catch (e) {
|
|
logger.error("Error while calling default command %s %s", this.defaultCommand, e);
|
|
return this.cmd_help();
|
|
}
|
|
} else {
|
|
logger.debug("Unrecognized command %s", command);
|
|
return this.cmd_help();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Basic cmd methods */
|
|
|
|
/*
|
|
return basic help information,.
|
|
*/
|
|
cmd_help(...args) {
|
|
return message.createBasic(this.helpAndUsage);
|
|
}
|
|
|
|
/**
|
|
* Return the basic config file
|
|
*
|
|
* @param {...any} args
|
|
*/
|
|
cmd_config(...args) {
|
|
if (this._config != null) {
|
|
let configBody = JSON.stringify(sanitizeConfig(this._config, this.getConfigSensitiveFields()), null, 2)
|
|
return message.createMarkdown(configBody, "```" + configBody + "```");
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
let abstractModulePrototype = Object.getPrototypeOf(new AbstractModule('', '', ''));
|
|
|
|
/*
|
|
Initialization of a module.
|
|
*/
|
|
function init(mod, globalConfig) {
|
|
logger.debug("Initializing module %s", mod.name);
|
|
if (mod.needConfig) {
|
|
logger.debug("Loading config file %s", mod.getConfigFilePath());
|
|
try {
|
|
mod._config = getConfig(mod.getConfigFilePath(), mod.getConfigSensitiveFields());
|
|
} catch (e) {
|
|
logger.error("Module %s needs a valid config file at %s", mod.name, mod.getConfigFilePath());
|
|
process.exit(1);
|
|
}
|
|
}
|
|
if (mod.needGlobalConfig) {
|
|
logger.debug("Bound global config to module %s", mod.name);
|
|
mod._global_config = globalConfig;
|
|
}
|
|
logger.debug("Detecting command methods.");
|
|
let commandMethods = getObjectKeysToPrototype(mod, abstractModulePrototype, (key) => {
|
|
return key.startsWith('cmd_') && isFunction(mod[key]);
|
|
})
|
|
// let commandMethods = objectKeys.filter();
|
|
logger.debug("Identified command methods: %s", commandMethods);
|
|
commandMethods.forEach((commandMethodName) => {
|
|
let command = commandMethodName.substring(4);
|
|
mod.addRecognizedCommand(command, commandMethodName);
|
|
})
|
|
logger.debug("Bound command methods for %s as %s", mod.name, mod.getRecognizedCommands());
|
|
}
|
|
|
|
exports.AbstractModule = AbstractModule
|
|
exports.initModule = init;
|