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.

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