Baphomet is the dedicated bot for nulloctet matrix
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.
 
 
 
 
 

260 lines
7.8 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: string = "AbstractModule";
/*
Short description of the module functionality.
*/
description: string = "Base Module That All Other Modules Extend";
/*
A helpful multiline string that defines the module usage
*/
helpAndUsage: string = `Example: !abstract_module <command>
!abstract_module <boo> : scares people`
/*
The exported command used to invoke the module directly.
*/
command: string = "abstract_module";
/*
The default method to call when a command word is not recognized.
*/
defaultCommand?: string = null;
/*
The module should be hidden from help and command dialogs.
*/
hidden: boolean = false;
/*
This module should receive all messages, regardless of whether
the module was directly referenced with a command.
*/
canHandleIndirectMessages: boolean = false;
/*
Indicates if the modules needs access to the global config
*/
needGlobalConfig: boolean = false;
/*
Indicates if the module requires a readable config file.
*/
needConfig: boolean = false;
/* internal */
/*
The global config passed in
*/
_global_config: any = null;
/*
The loaded config file, if it exists.
*/
_config: any = null;
/**
* The recognized commands for the module
*/
_recognizedCommands: Array<string> = [];
/**
* The map of commands to functions
*/
_recognizedCommandMap: Map<string, string> = new Map();
constructor(name: string, description: string, command: string) {
this.name = name;
this.description = description;
this.command = command;
}
/**
* Called after the module is initialized.
*/
postInit() : any {
return this;
}
/**
* Adds a recognized command and method to the module.
*
* @param {*} command
* @param {*} methodName
*/
addRecognizedCommand(command: string, methodName: string) {
this._recognizedCommands.push(command);
this._recognizedCommandMap.set(command, methodName);
}
/**
* Returns the list of recognized commands.
*/
getRecognizedCommands() : Iterable<string> {
return this._recognizedCommandMap.keys();
}
/**
* The file path that the module configuration file is expected at.
*/
getConfigFilePath() : string {
return process.env.NODE_PATH + '/data/' + this.name.toLowerCase().replace(' ', '_') + '-config.json';
}
/**
* Fields that should be sanitized before printing the config information for the user.
*/
getConfigSensitiveFields() : Array<string> {
return [];
}
/**
* Return a global config value or a default.
*
* @param {*} key
* @param {*} defaultValue
*/
getGlobal(key: string, defaultValue: string = null) {
if (this._global_config !== null && typeof this._global_config[key] !== 'undefined') {
return this._global_config[key];
}
return defaultValue
}
/**
* Return a module config value or a default.
*
* @param {*} key
* @param {*} defaultValue
*/
get(key: string, defaultValue: string = 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: any, room: any, callback: CallableFunction) {
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("[%s] Attempting to call %s with %s", trigger, command, args);
let responseMessage = this.processMessage(event, command, ...args);
callback(
room,
responseMessage
);
}
/*
Call the command method with the args
*/
processMessage(event: any, command: string, ...args: Array<string>) {
if (this._recognizedCommands.includes(command)) {
logger.debug("Calling %s with %s", this._recognizedCommandMap.get(command), args);
return this[this._recognizedCommandMap.get(command)](event, ...args);
} else {
if (this.defaultCommand != null) {
logger.debug("Attempting to use default command %s", this.defaultCommand);
let newArgs = [command].concat(...args);
try {
logger.debug("Calling %s with %s", this._recognizedCommandMap.get(this.defaultCommand), newArgs);
return this[this._recognizedCommandMap.get(this.defaultCommand)](event, ...newArgs);
} catch (e) {
logger.error("Error while calling default command %s %s", this.defaultCommand, e);
return this.cmd_help(event, ...newArgs);
}
} else {
logger.debug("Unrecognized command %s", command);
return this.cmd_help(event, ...[command].concat(...args));
}
}
}
/* Basic cmd methods */
/*
return basic help information,.
*/
cmd_help(event: any, ...args: Array<string>) {
return message.createBasic(this.helpAndUsage);
}
/**
* Return the basic config file
*
* @param {...any} args
*/
cmd_config(event: any, ...args: Array<string>) {
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: AbstractModule, globalConfig: any) {
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());
mod.postInit();
}
export { AbstractModule };
export { init as initModule };