Browse Source

Started work on admin module

* Added config command as default
* Added markdown message type with auto conversion
* Added example module
* Added sanitized global config loading for modules
develop
Drew Short 4 years ago
parent
commit
4a3d209e52
  1. 3
      bot/config.js
  2. 26
      bot/engine.js
  3. 18
      bot/message.js
  4. 45
      bot/module/abstract.js
  5. 37
      bot/module/admin.js
  6. 37
      bot/module/example.js
  7. 8
      bot/module/giphy.js
  8. 2
      bot/module/help.js
  9. 113
      package-lock.json
  10. 1
      package.json

3
bot/config.js

@ -25,4 +25,5 @@ function getConfig(configFile, sanitizedFields = [], reload = false) {
}
}
exports.getConfig = getConfig
exports.getConfig = getConfig;
exports.sanitizeConfig = sanitizeConfig;

26
bot/engine.js

@ -6,6 +6,7 @@ let help = require('./module/help');
let message = require('./message');
let utility = require('./utility');
let { initModule } = require('./module/abstract');
let { sanitizeConfig } = require('./config');
let commandPrefix = '!';
@ -21,9 +22,16 @@ class Engine {
}
initModules() {
let sanitizedGlobalConfig = sanitizeConfig(
this.config,
[
'userPassword',
'accessToken'
])
this.modules.forEach((mod) => {
logger.info("Loading module: %s", mod.name);
initModule(mod);
initModule(mod, sanitizedGlobalConfig);
logger.info("Recognized commands: %s", mod.getRecognizedCommands())
this.moduleMap.set(mod.command, mod);
this.commandMap.set(mod.command, mod);
@ -118,13 +126,17 @@ class Engine {
function sendResponseMessage(bot, room, responseMessage) {
logger.debug("Responding to room: %s with %o", room.roomId, responseMessage);
Promise.resolve(responseMessage).then((promisedMessage) => {
logger.debug("Sending message: %s", promisedMessage);
if (promisedMessage instanceof Object) {
bot.client.sendMessage(room.roomId, promisedMessage);
} else if (utility.isString(promisedMessage)) {
bot.client.sendMessage(room.roomId, message.createBasic(promisedMessage));
if (responseMessage !== null) {
logger.debug("Sending message: %s", promisedMessage);
if (promisedMessage instanceof Object) {
bot.client.sendMessage(room.roomId, promisedMessage);
} else if (utility.isString(promisedMessage)) {
bot.client.sendMessage(room.roomId, message.createBasic(promisedMessage));
} else {
logger.error("Unable to process response message: %s", promisedMessage);
}
} else {
logger.error("Unable to process response message: %s", promisedMessage);
logger.warn("No response message offered");
}
})
}

18
bot/message.js

@ -1,3 +1,7 @@
let showdown = require('showdown');
let converter = new showdown.Converter();
let messageTypes = {
TEXT: 'm.text',
NOTICE: 'm.notice'
@ -7,8 +11,18 @@ function createBasicMessage(body, msgtype = messageTypes.TEXT) {
return {
"body": body,
"msgtype": msgtype
}
};
}
function createMarkdownMessage(body, markdown, msgtype = messageTypes.TEXT) {
return {
"body": body,
"msgtype": msgtype,
"format": "org.matrix.custom.html",
"formatted_body": converter.makeHtml(markdown)
};
}
exports.types = messageTypes;
exports.createBasic = createBasicMessage;
exports.createBasic = createBasicMessage;
exports.createMarkdown = createMarkdownMessage;

45
bot/module/abstract.js

@ -5,7 +5,7 @@
let { logger } = require('../logging');
let message = require('../message');
let { isFunction, getObjectKeysToPrototype } = require('../utility');
let { getConfig } = require('../config');
let { getConfig, sanitizeConfig } = require('../config');
class AbstractModule {
/*
@ -45,6 +45,11 @@ class AbstractModule {
*/
canHandleIndirectMessages = false;
/*
Indicates if the modules needs access to the global config
*/
needGlobalConfig = false;
/*
Indicates if the module requires a readable config file.
*/
@ -52,6 +57,11 @@ class AbstractModule {
/* internal */
/*
The global config passed in
*/
_global_config = null;
/*
The loaded config file, if it exists.
*/
@ -82,6 +92,20 @@ class AbstractModule {
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.
*
@ -141,6 +165,19 @@ class AbstractModule {
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('', '', ''));
@ -148,7 +185,7 @@ let abstractModulePrototype = Object.getPrototypeOf(new AbstractModule('', '', '
/*
Initialization of a module.
*/
function init(mod) {
function init(mod, globalConfig) {
logger.debug("Initializing module %s", mod.name);
if (mod.needConfig) {
logger.debug("Loading config file %s", mod.getConfigFilePath());
@ -159,6 +196,10 @@ function init(mod) {
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]);

37
bot/module/admin.js

@ -3,6 +3,9 @@
*/
let { AbstractModule } = require('./abstract');
let { logger } = require('../logging');
let { sanitizeConfig } = require('../config');
let message = require('../message');
class AdminModule extends AbstractModule {
constructor() {
@ -11,8 +14,40 @@ class AdminModule extends AbstractModule {
"Support administration tasks",
"admin"
);
this.hidden = true;
this.helpAndUsage = `Usage: admin <command>
...`;
admin config - print the bot configuration`;
this.needGlobalConfig = true;
}
/**
* Override to only permit recognized admin users to access the plugin
*/
handleMessage(event, room, callback) {
if (this.getGlobal("admins", []).includes(event.sender.userId)) {
logger.debug("Authorized %s for admin action", event.sender.userId);
super.handleMessage(event, room, callback);
} else {
logger.warn("User %s tried to access admin functionality", event.sender.userId);
return event.sender.userId + " is not a recognized admin!"
}
}
/* Commands
* All methods starting with cmd_ will be parsed at initialization to expose those methods as commdands to the user
*/
/**
* Print the current config information
*
* @param {...any} args
*/
cmd_config(...args) {
if (this._global_config != null) {
let configBody = JSON.stringify(sanitizeConfig(this._global_config), null, 2)
return message.createMarkdown(configBody, "```" + configBody + "```");
}
return null;
}
}

37
bot/module/example.js

@ -0,0 +1,37 @@
/**
* Example module
* Copy this, replace the relevent parts, add it to the list in index.js
*/
let { AbstractModule } = require('./abstract');
class ExampleModule extends AbstractModule {
constructor() {
super(
"Example",
"Example tasks",
"example"
);
this.helpAndUsage = `Usage: example <command>
...`;
this.hidden = false; //default value
this.needGlobalConfig = false; // default value
this.needConfig = false; // default value
this.defaultCommand = 'search'; // name of the command to call when no recognized command name is present
}
/* Commands
* All methods starting with cmd_ will be parsed at initialization to expose those methods as commdands to the user
*/
/**
* Processed when !example blah is processed
*
* @param {...any} args the individual words passed after the command
*/
cmd_blah(...args) {
return "Bla blah bla"
}
}
exports.module = new ExampleModule();

8
bot/module/giphy.js

@ -24,11 +24,15 @@ class GiphyModule extends AbstractModule {
}
getGiphySearch(term) {
let url = this._config.endpoint + '/gifs/search?api_key=' + this._config.apiKey + '&q=' + term + '&limit=1';
logger.debug("Requesting: %s", url.replace(this._config.apiKey, '******'));
let url = this.get("endpoint") + '/gifs/search?api_key=' + this.get("apiKey") + '&q=' + term + '&limit=1';
logger.debug("Requesting: %s", url.replace(this.get("apiKey"), '******'));
return axios.get(url);
}
/* Commands
* All methods starting with cmd_ will be parsed at initialization to expose those methods as commdands to the user
*/
/**
* Return the top item for the search terms.
*

2
bot/module/help.js

@ -25,6 +25,8 @@ class HelpModule extends AbstractModule {
return help;
}
/* Commands */
cmd_help(...args) {
logger.debug("%o", args)
if (args.length < 1) {

113
package-lock.json

@ -36,7 +36,6 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
@ -163,8 +162,7 @@
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"caseless": {
"version": "0.12.0",
@ -197,7 +195,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
"dev": true,
"requires": {
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
@ -207,14 +204,12 @@
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
@ -225,7 +220,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
@ -331,8 +325,7 @@
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"dev": true
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"define-properties": {
"version": "1.1.3",
@ -376,8 +369,7 @@
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
"dev": true
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"enabled": {
"version": "1.0.2",
@ -467,7 +459,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
@ -534,8 +525,7 @@
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"getpass": {
"version": "0.1.7",
@ -656,8 +646,7 @@
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"dev": true
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"is-regex": {
"version": "1.0.4",
@ -756,7 +745,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
@ -950,7 +938,6 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
"integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
@ -959,7 +946,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
@ -967,14 +953,12 @@
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
},
"path-is-absolute": {
"version": "1.0.1",
@ -1059,14 +1043,12 @@
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"dev": true
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"safe-buffer": {
"version": "5.2.0",
@ -1087,8 +1069,67 @@
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
"dev": true
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"showdown": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz",
"integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==",
"requires": {
"yargs": "^14.2"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
},
"yargs": {
"version": "14.2.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.2.tgz",
"integrity": "sha512-/4ld+4VV5RnrynMhPZJ/ZpOCGSCeghMykZ3BhdFBDa9Wy/RH6uEGNWDJog+aUlq+9OM1CFTgtYRW5Is1Po9NOA==",
"requires": {
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^15.0.0"
}
},
"yargs-parser": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz",
"integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"simple-swizzle": {
"version": "0.2.2",
@ -1276,8 +1317,7 @@
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
"dev": true
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wide-align": {
"version": "1.1.3",
@ -1346,7 +1386,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
@ -1356,14 +1395,12 @@
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"dev": true
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
@ -1374,7 +1411,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
@ -1390,8 +1426,7 @@
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yargs": {
"version": "13.3.0",

1
package.json

@ -12,6 +12,7 @@
"dependencies": {
"axios": "^0.19.0",
"matrix-js-sdk": "2.4.5",
"showdown": "^1.9.1",
"trie-prefix-tree": "^1.5.1",
"winston": "^3.2.1"
},

Loading…
Cancel
Save