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.

175 lines
5.3 KiB

  1. /**
  2. * Base module that all modules extend
  3. */
  4. let { logger } = require('../logging');
  5. let message = require('../message');
  6. let { isFunction, getObjectKeysToPrototype } = require('../utility');
  7. let { getConfig } = require('../config');
  8. class AbstractModule {
  9. /*
  10. Name of the module used in help documentation and logging.
  11. */
  12. name = "AbstractModule";
  13. /*
  14. Short description of the module functionality.
  15. */
  16. description = "Base Module That All Other Modules Extend";
  17. /*
  18. A helpful multiline string that defines the module usage
  19. */
  20. helpAndUsage = `Example: !abstract_module <command>
  21. !abstract_module <boo> : scares people`
  22. /*
  23. The exported command used to invoke the module directly.
  24. */
  25. command = "abstract_module";
  26. /*
  27. The default method to call when a command word is not recognized.
  28. */
  29. defaultCommand = null;
  30. /*
  31. The module should be hidden from help and command dialogs.
  32. */
  33. hidden = false;
  34. /*
  35. This module should receive all messages, regardless of whether
  36. the module was directly referenced with a command.
  37. */
  38. canHandleIndirectMessages = false;
  39. /*
  40. Indicates if the module requires a readable config file.
  41. */
  42. needConfig = false;
  43. /* internal */
  44. /*
  45. The loaded config file, if it exists.
  46. */
  47. _config = null;
  48. constructor(name, description, command) {
  49. this.name = name;
  50. this.description = description;
  51. this.command = command;
  52. this._recognizedCommands = [];
  53. this._recognizedCommandMap = new Map();
  54. }
  55. addRecognizedCommand(command, methodName) {
  56. this._recognizedCommands.push(command);
  57. this._recognizedCommandMap.set(command, methodName);
  58. }
  59. getRecognizedCommands() {
  60. return this._recognizedCommandMap.keys();
  61. }
  62. getConfigFilePath() {
  63. return process.env.NODE_PATH + '/data/' + this.name.toLowerCase().replace(' ', '_') + '-config.json';
  64. }
  65. getConfigSensitiveFields() {
  66. return [];
  67. }
  68. /**
  69. * Default functionality for receiving and processing a message.
  70. *
  71. * Override this if the module needs to do more complicated message processing.
  72. */
  73. handleMessage(event, room, callback) {
  74. logger.debug("[%s] [%s] [%s]", this.name, room.name, event.event.content.body);
  75. let messageBody = event.event.content.body;
  76. let bodyParts = messageBody.split(' ');
  77. let trigger = bodyParts[0];
  78. let command = bodyParts[1];
  79. var args = [];
  80. if (bodyParts.length > 2) {
  81. args = bodyParts.slice(2);
  82. }
  83. logger.debug("Attempting to call %s with %s", command, args);
  84. let responseMessage = this.processMessage(command, ...args);
  85. callback(
  86. room,
  87. responseMessage
  88. );
  89. }
  90. /*
  91. Call the command method with the args
  92. */
  93. processMessage(command, ...args) {
  94. if (this._recognizedCommands.includes(command)) {
  95. logger.debug("Calling %s with %s", this._recognizedCommandMap.get(command), args);
  96. return this[this._recognizedCommandMap.get(command)](...args);
  97. } else {
  98. if (this.defaultCommand != null) {
  99. logger.debug("Attempting to use default command %s", this.defaultCommand);
  100. try {
  101. let newArgs = [command].concat(...args);
  102. logger.debug("Calling %s with %s", this._recognizedCommandMap.get(this.defaultCommand), newArgs);
  103. return this[this._recognizedCommandMap.get(this.defaultCommand)](...newArgs);
  104. } catch (e) {
  105. logger.error("Error while calling default command %s %s", this.defaultCommand, e);
  106. return this.cmd_help();
  107. }
  108. } else {
  109. logger.debug("Unrecognized command %s", command);
  110. return this.cmd_help();
  111. }
  112. }
  113. }
  114. /* Basic cmd methods */
  115. /*
  116. return basic help information,.
  117. */
  118. cmd_help(...args) {
  119. return message.createBasic(this.helpAndUsage);
  120. }
  121. }
  122. let abstractModulePrototype = Object.getPrototypeOf(new AbstractModule('', '', ''));
  123. /*
  124. Initialization of a module.
  125. */
  126. function init(mod) {
  127. logger.debug("Initializing module %s", mod.name);
  128. if (mod.needConfig) {
  129. logger.debug("Loading config file %s", mod.getConfigFilePath());
  130. try {
  131. mod._config = getConfig(mod.getConfigFilePath(), mod.getConfigSensitiveFields());
  132. } catch (e) {
  133. logger.error("Module %s needs a valid config file at %s", mod.name, mod.getConfigFilePath());
  134. process.exit(1);
  135. }
  136. }
  137. logger.debug("Detecting command methods.");
  138. let commandMethods = getObjectKeysToPrototype(mod, abstractModulePrototype, (key) => {
  139. return key.startsWith('cmd_') && isFunction(mod[key]);
  140. })
  141. // let commandMethods = objectKeys.filter();
  142. logger.debug("Identified command methods: %s", commandMethods);
  143. commandMethods.forEach((commandMethodName) => {
  144. let command = commandMethodName.substring(4);
  145. mod.addRecognizedCommand(command, commandMethodName);
  146. })
  147. logger.debug("Bound command methods for %s as %s", mod.name, mod.getRecognizedCommands());
  148. }
  149. exports.AbstractModule = AbstractModule
  150. exports.initModule = init;