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.

158 lines
5.6 KiB

  1. let { logger } = require('./logging');
  2. let { modules } = require('./module/index');
  3. let trie = require('trie-prefix-tree');
  4. let { getShortestPrefix } = require('./utility');
  5. let help = require('./module/help');
  6. let message = require('./message');
  7. let utility = require('./utility');
  8. let { initModule } = require('./module/abstract');
  9. let { sanitizeConfig } = require('./config');
  10. let commandPrefix = '!';
  11. class Engine {
  12. constructor(config, bot, modules) {
  13. this.config = config;
  14. this.bot = bot;
  15. this.modules = modules;
  16. this.moduleMap = new Map();
  17. this.commands = [];
  18. this.commandMap = new Map();
  19. this.commandRadixTree = trie([]);
  20. }
  21. initModules() {
  22. let sanitizedGlobalConfig = sanitizeConfig(
  23. this.config,
  24. [
  25. 'userPassword',
  26. 'accessToken'
  27. ])
  28. this.modules.forEach((mod) => {
  29. logger.info("Loading module: %s", mod.name);
  30. initModule(mod, sanitizedGlobalConfig);
  31. logger.info("Recognized commands: %s", mod.getRecognizedCommands())
  32. this.moduleMap.set(mod.command, mod);
  33. this.commandMap.set(mod.command, mod);
  34. this.commandRadixTree.addWord(mod.command);
  35. });
  36. this.modules.forEach((mod) => {
  37. let shortCharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 1);
  38. let short3CharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 3);
  39. let shortCommandAliases = [shortCharCommand, short3CharCommand];
  40. logger.info("Adding short command %s for module: %s", shortCommandAliases, mod.name);
  41. shortCommandAliases.forEach((commandAlias) => {
  42. this.commandMap.set(commandAlias, mod);
  43. })
  44. });
  45. this.helpModule = help.create(this.moduleMap)
  46. initModule(this.helpModule);
  47. this.moduleMap.set(this.helpModule.command, this.helpModule);
  48. this.commandMap.set('help', this.helpModule);
  49. this.commands = Array.from(this.commandMap.keys()).sort()
  50. logger.info("Bound modules to keywords: %o", this.moduleMap);
  51. }
  52. init() {
  53. logger.info("Initializing modules");
  54. this.initModules();
  55. /* Bind Message Parsing */
  56. let engine = this;
  57. let handleEvent = (event, room, toStartOfTimeline) => {
  58. /* Don't process messages from self */
  59. if (event.sender.userId !== this.config.userId) {
  60. /* don't process messages that aren't of type m.room.message */
  61. if (event.getType() !== "m.room.message") {
  62. logger.debug("Recieved message of type: %s", event.getType());
  63. return;
  64. } else {
  65. let messageBody = event.event.content.body;
  66. logger.debug("[%s] %s", room.name, messageBody);
  67. if (messageBody.indexOf(commandPrefix) === 0) {
  68. let command = messageBody.split(' ')[0].substring(1);
  69. if (engine.commandMap.has(command)) {
  70. engine.commandMap.get(command).handleMessage(event, room, sendResponseMessageCallback(engine.bot));
  71. } else {
  72. let responseMessage = "The following commands are recognized"
  73. responseMessage += "\n" + engine.commands.join(", ")
  74. responseMessage += "\nAdditional information can be discovered with !help <command>"
  75. sendResponseMessage(engine.bot, room, responseMessage);
  76. }
  77. }
  78. }
  79. }
  80. }
  81. this.bot.init(handleEvent);
  82. /* Capture Exit Conditions */
  83. ["SIGINT", "SIGTERM"].forEach((signature) => {
  84. process.on(signature, async () => {
  85. await this.bot.sendStatusShutdown()
  86. .then(() => {
  87. logger.info("Gracefully stopping Matrix SDK Client")
  88. this.bot.client.stopClient();
  89. });
  90. process.exit(0);
  91. });
  92. });
  93. process.on('exit', () => {
  94. logger.info("Shutting Down");
  95. });
  96. return this;
  97. }
  98. run() {
  99. this.bot.connect();
  100. return this;
  101. }
  102. }
  103. /**
  104. * Handle the callback sending messages via the bot
  105. *
  106. * @param {*} bot
  107. * @param {*} room
  108. * @param {*} responseMessage
  109. */
  110. function sendResponseMessage(bot, room, responseMessage) {
  111. logger.debug("Responding to room: %s with %o", room.roomId, responseMessage);
  112. Promise.resolve(responseMessage).then((promisedMessage) => {
  113. if (responseMessage !== null) {
  114. logger.debug("Sending message: %s", promisedMessage);
  115. if (promisedMessage instanceof Object) {
  116. bot.client.sendMessage(room.roomId, promisedMessage);
  117. } else if (utility.isString(promisedMessage)) {
  118. bot.client.sendMessage(room.roomId, message.createBasic(promisedMessage));
  119. } else {
  120. logger.error("Unable to process response message: %s", promisedMessage);
  121. }
  122. } else {
  123. logger.warn("No response message offered");
  124. }
  125. })
  126. }
  127. /**
  128. * Wrapper to produce a callback function that can be passed to the modules
  129. *
  130. * @param {*} bot
  131. */
  132. function sendResponseMessageCallback(bot) {
  133. return (room, responseMessage) => {
  134. sendResponseMessage(bot, room, responseMessage);
  135. }
  136. }
  137. function create(config, bot) {
  138. return new Engine(config, bot, modules)
  139. }
  140. exports.create = create;