From c8f86ae762f1f9550fd368be403c1209d5b41262 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 12:41:39 -0600 Subject: [PATCH 01/26] Adding shutdown hook * Migrated some code into Bot class * Added shutdown hook with async pattern to send shutdown message * Added utility module * Added buildInfo version on startup and in startup message --- bot/bot.js | 99 +++++++++++++++++++++++++++++++++++++++++--------- bot/utility.js | 18 +++++++++ index.js | 6 ++- 3 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 bot/utility.js diff --git a/bot/bot.js b/bot/bot.js index 6a38a1b..42d7c28 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -1,9 +1,59 @@ +let fs = require('fs'); let sdk = require("matrix-js-sdk"); let message = require("./message.js"); +let utility = require("./utility.js"); class Bot { - constructor(config) { - this.config = config + constructor(config, buildInfo) { + this.config = config; + this.buildInfo = buildInfo; + this.connected = false; + } + + sendStatusStartup() { + let promises = [Promise.resolve(true)] + if (this.connected) { + this.config.statusRooms.forEach(roomId => { + console.log("Notifying %s", roomId, "of startup"); + promises.push(this.client.sendMessage( + roomId, message.createBasic("Started with version: " + this.buildInfo) + ).then(function () { + console.log("Notified %s", roomId, "of startup"); + })); + }); + } else { + console.log("Attempting to send startup message while disconnected"); + } + return Promise.all(promises); + } + + sendStatusShutdown() { + let promises = [Promise.resolve(true)] + if (this.connected) { + this.config.statusRooms.forEach(roomId => { + console.log("Notifying %s", roomId, "of Shutdown"); + promises.push(this.client.sendMessage( + roomId, message.createBasic("Shutting down") + ).then(function () { + console.log("Notified %s", roomId, "of shutdown"); + })); + }); + } else { + console.log("Attempting to send shutdown message while disconnected"); + } + return Promise.all(promises); + } +} + +function getBuildInfo(buildInfoPath) { + try { + return fs.readFileSync(buildInfoPath, "utf8"); + } catch (err) { + if (err.code === 'ENOENT') { + return "UNKNOWN_" + utility.toISODateString(new Date()); + } else { + console.log("Unexpected Error! " + err); + } } } @@ -11,7 +61,10 @@ function create(configFile) { let config = require(configFile); console.log("Running with config:"); console.log(config); - return new Bot(config); + let buildInfo = getBuildInfo("../build.info") + console.log("Running version:"); + console.log(buildInfo); + return new Bot(config, buildInfo); } function init(bot) { @@ -22,39 +75,49 @@ function init(bot) { userId: bot.config.userId }); - function sendClientStatusUpdate() { - bot.config.statusRooms.forEach(roomId => { - console.log("Notifying %s", roomId); - bot.client.sendMessage(roomId, message.createBasic("Started!")).done(function () { - console.log("Notified %s", roomId); - }) - }); - } - - // Prep the bot - bot.client.on("sync", function (state, previousState, data) { + bot.client.on("sync", async function (state, previousState, data) { switch (state) { case "PREPARED": - sendClientStatusUpdate(); + bot.connected = true; + await bot.sendStatusStartup(); break; } }); - // auto join rooms that an admin user has invited the bot to bot.client.on("RoomMember.membership", function (event, member) { if (member.membership === "invite" && bot.config.admin.indexOf(ember.userId) >= 0) { - bot.client.joinRoom(member.roomId).done(function () { + bot.client.joinRoom(member.roomId).done(function () { console.log("Auto-joined %s", member.roomId); }); } }); + /* Capture Exit Conditions */ + + ["SIGINT", "SIGTERM"].forEach((signature) => { + process.on(signature, async () => { + await bot.sendStatusShutdown() + .then(function () { + console.log("Gracefully stopping Matrix SDK Client") + bot.client.stopClient(); + }); + process.exit(0); + }); + }); + + process.on('exit', async function () { + console.log("Shutting Down"); + }); + return bot; } function run(bot) { - bot.client.startClient() + // console.log("Initializing Crypto"); + // await bot.client.initCrypto(); + console.log("Starting Matrix SDK Client"); + bot.client.startClient(); } exports.create = create; diff --git a/bot/utility.js b/bot/utility.js new file mode 100644 index 0000000..318467c --- /dev/null +++ b/bot/utility.js @@ -0,0 +1,18 @@ +function toISODateString(d) { + function pad(n) { return n < 10 ? '0' + n : n } + return d.getUTCFullYear() + '-' + + pad(d.getUTCMonth() + 1) + '-' + + pad(d.getUTCDate()) + 'T' + + pad(d.getUTCHours()) + ':' + + pad(d.getUTCMinutes()) + ':' + + pad(d.getUTCSeconds()) + 'Z' +} + +function sleep(ms){ + return new Promise(resolve=>{ + setTimeout(resolve,ms) + }) +} + +exports.toISODateString = toISODateString; +exports.sleep = sleep; \ No newline at end of file diff --git a/index.js b/index.js index 98f636c..51fb774 100644 --- a/index.js +++ b/index.js @@ -1,2 +1,6 @@ let bot = require('./bot/bot.js'); -bot.run(bot.init(bot.create("../data/config.json"))); +bot.run( + bot.init( + bot.create("../data/config.json") + ) +); From b2c33f5ab9cfc593bbff25e81dcdefe7c8ac923b Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 13:04:10 -0600 Subject: [PATCH 02/26] Adding a logging library --- .dockerignore | 3 +- .gitignore | 1 + bot/bot.js | 46 +++++---- bot/logging.js | 23 +++++ combined.log | 7 ++ error.log | 0 package-lock.json | 236 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 3 +- 8 files changed, 288 insertions(+), 31 deletions(-) create mode 100644 bot/logging.js create mode 100644 combined.log create mode 100644 error.log diff --git a/.dockerignore b/.dockerignore index b497360..f49b408 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,2 @@ -data/config.json \ No newline at end of file +data/config.json +data/*.log \ No newline at end of file diff --git a/.gitignore b/.gitignore index af95236..41cef64 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ node_modules/ # Config files data/config.json +data/*.log # Mac Files .DS_Store \ No newline at end of file diff --git a/bot/bot.js b/bot/bot.js index 42d7c28..e026e93 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -1,7 +1,8 @@ let fs = require('fs'); -let sdk = require("matrix-js-sdk"); -let message = require("./message.js"); -let utility = require("./utility.js"); +let sdk = require('matrix-js-sdk'); +let message = require('./message.js'); +let utility = require('./utility.js'); +let logging = require('./logging'); class Bot { constructor(config, buildInfo) { @@ -14,15 +15,15 @@ class Bot { let promises = [Promise.resolve(true)] if (this.connected) { this.config.statusRooms.forEach(roomId => { - console.log("Notifying %s", roomId, "of startup"); + logging.logger.debug("Notifying %s", roomId, "of startup"); promises.push(this.client.sendMessage( roomId, message.createBasic("Started with version: " + this.buildInfo) ).then(function () { - console.log("Notified %s", roomId, "of startup"); + logging.logger.debug("Notified %s", roomId, "of startup"); })); }); } else { - console.log("Attempting to send startup message while disconnected"); + logging.logger.warn("Attempting to send startup message while disconnected"); } return Promise.all(promises); } @@ -31,15 +32,15 @@ class Bot { let promises = [Promise.resolve(true)] if (this.connected) { this.config.statusRooms.forEach(roomId => { - console.log("Notifying %s", roomId, "of Shutdown"); + logging.logger.debug("Notifying %s", roomId, "of Shutdown"); promises.push(this.client.sendMessage( roomId, message.createBasic("Shutting down") ).then(function () { - console.log("Notified %s", roomId, "of shutdown"); + logging.logger.debug("Notified %s", roomId, "of shutdown"); })); }); } else { - console.log("Attempting to send shutdown message while disconnected"); + logging.logger.warn("Attempting to send shutdown message while disconnected"); } return Promise.all(promises); } @@ -52,23 +53,28 @@ function getBuildInfo(buildInfoPath) { if (err.code === 'ENOENT') { return "UNKNOWN_" + utility.toISODateString(new Date()); } else { - console.log("Unexpected Error! " + err); + logging.logger.error("Unexpected Error! " + err); } } } +function sanitizeConfig(config) { + let clonedConfig = { ...config }; + clonedConfig.accessToken = "******" + return clonedConfig; +} + function create(configFile) { let config = require(configFile); - console.log("Running with config:"); - console.log(config); + logging.logger.info("Running with config:"); + logging.logger.debug(sanitizeConfig(config)); let buildInfo = getBuildInfo("../build.info") - console.log("Running version:"); - console.log(buildInfo); + logging.logger.info("Running version:", buildInfo); return new Bot(config, buildInfo); } function init(bot) { - console.log("Creating Matrix Client") + logging.logger.info("Creating Matrix Client") bot.client = sdk.createClient({ baseUrl: bot.config.baseUrl, accessToken: bot.config.accessToken, @@ -88,7 +94,7 @@ function init(bot) { if (member.membership === "invite" && bot.config.admin.indexOf(ember.userId) >= 0) { bot.client.joinRoom(member.roomId).done(function () { - console.log("Auto-joined %s", member.roomId); + logging.logger.info("Auto-joined %s", member.roomId); }); } }); @@ -99,7 +105,7 @@ function init(bot) { process.on(signature, async () => { await bot.sendStatusShutdown() .then(function () { - console.log("Gracefully stopping Matrix SDK Client") + logging.logger.info("Gracefully stopping Matrix SDK Client") bot.client.stopClient(); }); process.exit(0); @@ -107,16 +113,16 @@ function init(bot) { }); process.on('exit', async function () { - console.log("Shutting Down"); + logging.logger.info("Shutting Down"); }); return bot; } function run(bot) { - // console.log("Initializing Crypto"); + // logging.logger.info("Initializing Crypto"); // await bot.client.initCrypto(); - console.log("Starting Matrix SDK Client"); + logging.logger.info("Starting Matrix SDK Client"); bot.client.startClient(); } diff --git a/bot/logging.js b/bot/logging.js new file mode 100644 index 0000000..64faeef --- /dev/null +++ b/bot/logging.js @@ -0,0 +1,23 @@ +let winston = require('winston'); + +let logger = winston.createLogger({ + level: 'info', + format: winston.format.json(), + defaultMeta: { service: 'user-service' }, + transports: [ + new winston.transports.File({ filename: 'error.log', level: 'error' }), + new winston.transports.File({ filename: 'combined.log' }) + ] +}); + +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.simple() + })); +} + +if (process.env.LOG_LEVEL !== '') { + logger.level = process.env.LOG_LEVEL +} + +exports.logger = logger; \ No newline at end of file diff --git a/combined.log b/combined.log new file mode 100644 index 0000000..1bfc68d --- /dev/null +++ b/combined.log @@ -0,0 +1,7 @@ +{"message":"Running with config:","level":"info","service":"user-service"} +{"message":{"baseUrl":"https://matrix.nulloctet.com","userId":"@baphomet-dev:nulloctet.com","accessToken":"******","admins":["@warrick:nulloctet.com"],"anyCanInvite":false,"statusRooms":["!ayFeLNSFDrerMMelCg:nulloctet.com"]},"level":"debug","service":"user-service"} +{"service":"user-service","level":"info","message":"Running version:"} +{"message":"Creating Matrix Client","level":"info","service":"user-service"} +{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} +{"service":"user-service","level":"debug","message":"Notifying %s"} +{"service":"user-service","level":"debug","message":"Notified %s"} diff --git a/error.log b/error.log new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index efcb174..16e15f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,14 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -215,11 +223,19 @@ } } }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -227,8 +243,35 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } }, "combined-stream": { "version": "1.0.8", @@ -296,6 +339,16 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -317,6 +370,19 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.x" + } + }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==" + }, "es-abstract": { "version": "1.16.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz", @@ -378,6 +444,16 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", @@ -521,8 +597,12 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "is-buffer": { "version": "2.0.4", @@ -557,6 +637,11 @@ "has": "^1.0.1" } }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -571,6 +656,11 @@ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -623,6 +713,14 @@ "verror": "1.10.0" } }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "requires": { + "colornames": "^1.1.1" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -636,8 +734,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "log-symbols": { "version": "2.2.0", @@ -648,6 +745,18 @@ "chalk": "^2.0.1" } }, + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } + }, "loglevel": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", @@ -741,8 +850,7 @@ "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "node-environment-flags": { "version": "1.0.5", @@ -802,6 +910,11 @@ "wrappy": "1" } }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, "p-limit": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", @@ -843,6 +956,11 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "psl": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz", @@ -858,6 +976,16 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==" }, + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", @@ -931,6 +1059,14 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -953,6 +1089,11 @@ "tweetnacl": "~0.14.0" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -983,6 +1124,14 @@ "function-bind": "^1.1.1" } }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -1007,6 +1156,11 @@ "has-flag": "^3.0.0" } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", @@ -1023,6 +1177,11 @@ } } }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -1049,6 +1208,11 @@ "punycode": "^2.1.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", @@ -1088,6 +1252,60 @@ "string-width": "^1.0.2 || 2" } }, + "winston": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", + "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "requires": { + "async": "^2.6.1", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^2.1.1", + "one-time": "0.0.4", + "readable-stream": "^3.1.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.3.0" + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", diff --git a/package.json b/package.json index 80d1249..d3d071f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "test": "mocha" }, "dependencies": { - "matrix-js-sdk": "2.4.5" + "matrix-js-sdk": "2.4.5", + "winston": "^3.2.1" }, "devDependencies": { "mocha": "^6.2.2" From 738a3cbc51bca3a1ad2ea0808c3c75c821da809f Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 13:22:23 -0600 Subject: [PATCH 03/26] Improved the logging configuration --- .dockerignore | 2 +- .gitignore | 4 +++- bot/bot.js | 34 +++++++++++++++++----------------- bot/logging.js | 15 ++++++++++----- combined.log | 29 +++++++++++++++++++++++++++++ 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/.dockerignore b/.dockerignore index f49b408..0b2e08c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,2 @@ data/config.json -data/*.log \ No newline at end of file +log/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 41cef64..cf70976 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,9 @@ node_modules/ # Config files data/config.json -data/*.log + +# Log files +log/ # Mac Files .DS_Store \ No newline at end of file diff --git a/bot/bot.js b/bot/bot.js index e026e93..dd44b4d 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -2,7 +2,7 @@ let fs = require('fs'); let sdk = require('matrix-js-sdk'); let message = require('./message.js'); let utility = require('./utility.js'); -let logging = require('./logging'); +let { logger } = require('./logging'); class Bot { constructor(config, buildInfo) { @@ -15,15 +15,15 @@ class Bot { let promises = [Promise.resolve(true)] if (this.connected) { this.config.statusRooms.forEach(roomId => { - logging.logger.debug("Notifying %s", roomId, "of startup"); + logger.debug("Notifying %s of startup", roomId); promises.push(this.client.sendMessage( roomId, message.createBasic("Started with version: " + this.buildInfo) ).then(function () { - logging.logger.debug("Notified %s", roomId, "of startup"); + logger.debug("Notified %s of startup", roomId); })); }); } else { - logging.logger.warn("Attempting to send startup message while disconnected"); + logger.warn("Attempting to send startup message while disconnected"); } return Promise.all(promises); } @@ -32,15 +32,15 @@ class Bot { let promises = [Promise.resolve(true)] if (this.connected) { this.config.statusRooms.forEach(roomId => { - logging.logger.debug("Notifying %s", roomId, "of Shutdown"); + logger.debug("Notifying %s of shutdown", roomId); promises.push(this.client.sendMessage( roomId, message.createBasic("Shutting down") ).then(function () { - logging.logger.debug("Notified %s", roomId, "of shutdown"); + logger.debug("Notified %s of shutdown", roomId); })); }); } else { - logging.logger.warn("Attempting to send shutdown message while disconnected"); + logger.warn("Attempting to send shutdown message while disconnected"); } return Promise.all(promises); } @@ -53,7 +53,7 @@ function getBuildInfo(buildInfoPath) { if (err.code === 'ENOENT') { return "UNKNOWN_" + utility.toISODateString(new Date()); } else { - logging.logger.error("Unexpected Error! " + err); + logger.error("Unexpected Error!", err); } } } @@ -66,15 +66,15 @@ function sanitizeConfig(config) { function create(configFile) { let config = require(configFile); - logging.logger.info("Running with config:"); - logging.logger.debug(sanitizeConfig(config)); + logger.info("Running with config:"); + logger.debug("%o", sanitizeConfig(config)); let buildInfo = getBuildInfo("../build.info") - logging.logger.info("Running version:", buildInfo); + logger.info("Running version: %s", buildInfo); return new Bot(config, buildInfo); } function init(bot) { - logging.logger.info("Creating Matrix Client") + logger.info("Creating Matrix Client") bot.client = sdk.createClient({ baseUrl: bot.config.baseUrl, accessToken: bot.config.accessToken, @@ -94,7 +94,7 @@ function init(bot) { if (member.membership === "invite" && bot.config.admin.indexOf(ember.userId) >= 0) { bot.client.joinRoom(member.roomId).done(function () { - logging.logger.info("Auto-joined %s", member.roomId); + logger.info("Auto-joined %s", member.roomId); }); } }); @@ -105,7 +105,7 @@ function init(bot) { process.on(signature, async () => { await bot.sendStatusShutdown() .then(function () { - logging.logger.info("Gracefully stopping Matrix SDK Client") + logger.info("Gracefully stopping Matrix SDK Client") bot.client.stopClient(); }); process.exit(0); @@ -113,16 +113,16 @@ function init(bot) { }); process.on('exit', async function () { - logging.logger.info("Shutting Down"); + logger.info("Shutting Down"); }); return bot; } function run(bot) { - // logging.logger.info("Initializing Crypto"); + // logger.info("Initializing Crypto"); // await bot.client.initCrypto(); - logging.logger.info("Starting Matrix SDK Client"); + logger.info("Starting Matrix SDK Client"); bot.client.startClient(); } diff --git a/bot/logging.js b/bot/logging.js index 64faeef..0c5c87b 100644 --- a/bot/logging.js +++ b/bot/logging.js @@ -2,17 +2,22 @@ let winston = require('winston'); let logger = winston.createLogger({ level: 'info', - format: winston.format.json(), - defaultMeta: { service: 'user-service' }, + format: winston.format.combine( + winston.format.splat(), + winston.format.json() + ), + defaultMeta: { service: 'baphomet-js' }, transports: [ - new winston.transports.File({ filename: 'error.log', level: 'error' }), - new winston.transports.File({ filename: 'combined.log' }) + new winston.transports.File({ filename: 'log/error.log', level: 'error' }), + new winston.transports.File({ filename: 'log/combined.log' }) ] }); if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ - format: winston.format.simple() + format: winston.format.combine( + winston.format.simple() + ) })); } diff --git a/combined.log b/combined.log index 1bfc68d..e754a7d 100644 --- a/combined.log +++ b/combined.log @@ -5,3 +5,32 @@ {"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} {"service":"user-service","level":"debug","message":"Notifying %s"} {"service":"user-service","level":"debug","message":"Notified %s"} +{"service":"user-service","level":"debug","message":"Notifying %s"} +{"service":"user-service","level":"debug","message":"Notified %s"} +{"message":"Running with config:","level":"info","service":"user-service"} +{"message":{"baseUrl":"https://matrix.nulloctet.com","userId":"@baphomet-dev:nulloctet.com","accessToken":"******","admins":["@warrick:nulloctet.com"],"anyCanInvite":false,"statusRooms":["!ayFeLNSFDrerMMelCg:nulloctet.com"]},"level":"debug","service":"user-service"} +{"service":"user-service","level":"info","message":"Running version:"} +{"message":"Creating Matrix Client","level":"info","service":"user-service"} +{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} +{"service":"user-service","level":"debug","message":"Notifying %s of startup"} +{"service":"user-service","level":"debug","message":"Notified %s of startup"} +{"service":"user-service","level":"debug","message":"Notifying %s of shutdown"} +{"service":"user-service","level":"debug","message":"Notified %s of shutdown"} +{"message":"Running with config:","level":"info","service":"user-service"} +{"message":{"baseUrl":"https://matrix.nulloctet.com","userId":"@baphomet-dev:nulloctet.com","accessToken":"******","admins":["@warrick:nulloctet.com"],"anyCanInvite":false,"statusRooms":["!ayFeLNSFDrerMMelCg:nulloctet.com"]},"level":"debug","service":"user-service"} +{"service":"user-service","level":"info","message":"Running version:"} +{"message":"Creating Matrix Client","level":"info","service":"user-service"} +{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} +{"service":"user-service","level":"debug","message":"Notifying %s of startup"} +{"service":"user-service","level":"debug","message":"Notified %s of startup"} +{"service":"user-service","level":"debug","message":"Notifying %s of shutdown"} +{"service":"user-service","level":"debug","message":"Notified %s of shutdown"} +{"message":"Running with config:","level":"info","service":"user-service"} +{"service":"user-service","level":"debug","message":"%o"} +{"service":"user-service","level":"info","message":"Running version: %s"} +{"message":"Creating Matrix Client","level":"info","service":"user-service"} +{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} +{"service":"user-service","level":"debug","message":"Notifying %s of startup"} +{"service":"user-service","level":"debug","message":"Notified %s of startup"} +{"service":"user-service","level":"debug","message":"Notifying %s of shutdown"} +{"service":"user-service","level":"debug","message":"Notified %s of shutdown"} From e92ebf6208d22fe0c35159a4c1231fb62cbf6737 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 13:28:19 -0600 Subject: [PATCH 04/26] Add module scaffold and anonymous function cleanup --- bot/bot.js | 21 +++++++++++---------- bot/module/index.js | 5 +++++ 2 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 bot/module/index.js diff --git a/bot/bot.js b/bot/bot.js index dd44b4d..67681bf 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -1,8 +1,9 @@ let fs = require('fs'); let sdk = require('matrix-js-sdk'); -let message = require('./message.js'); -let utility = require('./utility.js'); +let message = require('./message'); +let utility = require('./utility'); let { logger } = require('./logging'); +let { modules } = require('./module/index') class Bot { constructor(config, buildInfo) { @@ -18,7 +19,7 @@ class Bot { logger.debug("Notifying %s of startup", roomId); promises.push(this.client.sendMessage( roomId, message.createBasic("Started with version: " + this.buildInfo) - ).then(function () { + ).then(() => { logger.debug("Notified %s of startup", roomId); })); }); @@ -35,7 +36,7 @@ class Bot { logger.debug("Notifying %s of shutdown", roomId); promises.push(this.client.sendMessage( roomId, message.createBasic("Shutting down") - ).then(function () { + ).then(() => { logger.debug("Notified %s of shutdown", roomId); })); }); @@ -81,19 +82,19 @@ function init(bot) { userId: bot.config.userId }); - bot.client.on("sync", async function (state, previousState, data) { + bot.client.on("sync", (state, previousState, data) => { switch (state) { case "PREPARED": bot.connected = true; - await bot.sendStatusStartup(); + bot.sendStatusStartup(); break; } }); - bot.client.on("RoomMember.membership", function (event, member) { + bot.client.on("RoomMember.membership", (event, member) => { if (member.membership === "invite" && bot.config.admin.indexOf(ember.userId) >= 0) { - bot.client.joinRoom(member.roomId).done(function () { + bot.client.joinRoom(member.roomId).done(() => { logger.info("Auto-joined %s", member.roomId); }); } @@ -104,7 +105,7 @@ function init(bot) { ["SIGINT", "SIGTERM"].forEach((signature) => { process.on(signature, async () => { await bot.sendStatusShutdown() - .then(function () { + .then(() => { logger.info("Gracefully stopping Matrix SDK Client") bot.client.stopClient(); }); @@ -112,7 +113,7 @@ function init(bot) { }); }); - process.on('exit', async function () { + process.on('exit', () => { logger.info("Shutting Down"); }); diff --git a/bot/module/index.js b/bot/module/index.js new file mode 100644 index 0000000..15cad4b --- /dev/null +++ b/bot/module/index.js @@ -0,0 +1,5 @@ +function getModules() { + return []; +} + +exports.modules = getModules(); \ No newline at end of file From c6b4d7d3c40c1b48ef19e005942317e8e865f6ce Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 13:48:54 -0600 Subject: [PATCH 05/26] Adding admin module and changing startup to notice --- bot/bot.js | 18 ++++++++++++++---- bot/message.js | 10 ++++++++-- bot/module/admin.js | 13 +++++++++++++ bot/module/index.js | 8 +++++++- 4 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 bot/module/admin.js diff --git a/bot/bot.js b/bot/bot.js index 67681bf..d8d5b7f 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -6,10 +6,17 @@ let { logger } = require('./logging'); let { modules } = require('./module/index') class Bot { - constructor(config, buildInfo) { + constructor(config, buildInfo, modules) { this.config = config; this.buildInfo = buildInfo; this.connected = false; + this.modules = modules; + } + + initModules() { + modules.forEach((module) => { + logger.info("Loading module: %s", module.name); + }); } sendStatusStartup() { @@ -18,7 +25,7 @@ class Bot { this.config.statusRooms.forEach(roomId => { logger.debug("Notifying %s of startup", roomId); promises.push(this.client.sendMessage( - roomId, message.createBasic("Started with version: " + this.buildInfo) + roomId, message.createBasic("Started with version: " + this.buildInfo, message.types.NOTICE) ).then(() => { logger.debug("Notified %s of startup", roomId); })); @@ -35,7 +42,7 @@ class Bot { this.config.statusRooms.forEach(roomId => { logger.debug("Notifying %s of shutdown", roomId); promises.push(this.client.sendMessage( - roomId, message.createBasic("Shutting down") + roomId, message.createBasic("Shutting down", message.types.NOTICE) ).then(() => { logger.debug("Notified %s of shutdown", roomId); })); @@ -71,10 +78,13 @@ function create(configFile) { logger.debug("%o", sanitizeConfig(config)); let buildInfo = getBuildInfo("../build.info") logger.info("Running version: %s", buildInfo); - return new Bot(config, buildInfo); + return new Bot(config, buildInfo, modules); } function init(bot) { + logger.info("Initializing modules"); + bot.initModules(); + logger.info("Creating Matrix Client") bot.client = sdk.createClient({ baseUrl: bot.config.baseUrl, diff --git a/bot/message.js b/bot/message.js index a748109..9c936bf 100644 --- a/bot/message.js +++ b/bot/message.js @@ -1,8 +1,14 @@ -function createBasicMessage(body) { +let messageTypes = { + TEXT: 'm.text', + NOTICE: 'm.notice' +} + +function createBasicMessage(body, msgtype=messageTypes.TEXT) { return { "body": body, - "msgtype": "m.text" + "msgtype": msgtype } } +exports.types = messageTypes; exports.createBasic = createBasicMessage; \ No newline at end of file diff --git a/bot/module/admin.js b/bot/module/admin.js new file mode 100644 index 0000000..729e000 --- /dev/null +++ b/bot/module/admin.js @@ -0,0 +1,13 @@ +/** + * Administration module + */ + + class AdminModule { + constructor() { + this.name = "Administration" + this.description = "Support administration tasks" + this.command = "admin" + } + } + + exports.module = new AdminModule(); \ No newline at end of file diff --git a/bot/module/index.js b/bot/module/index.js index 15cad4b..1c36a30 100644 --- a/bot/module/index.js +++ b/bot/module/index.js @@ -1,5 +1,11 @@ +/** + * Manage the registered modules + */ + + let adminModule = require('./admin'); + function getModules() { - return []; + return [adminModule.module]; } exports.modules = getModules(); \ No newline at end of file From fa91dc468b8f79c52e15d39d63287484f906d70c Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 14:52:59 -0600 Subject: [PATCH 06/26] Code cleanup * Added engine module * Moved tools into utility module --- bot/bot.js | 76 ++------------------------------------------------ bot/engine.js | 61 ++++++++++++++++++++++++++++++++++++++++ bot/utility.js | 22 +++++++++++++-- index.js | 7 +++-- 4 files changed, 86 insertions(+), 80 deletions(-) create mode 100644 bot/engine.js diff --git a/bot/bot.js b/bot/bot.js index d8d5b7f..244d4db 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -1,5 +1,3 @@ -let fs = require('fs'); -let sdk = require('matrix-js-sdk'); let message = require('./message'); let utility = require('./utility'); let { logger } = require('./logging'); @@ -54,18 +52,6 @@ class Bot { } } -function getBuildInfo(buildInfoPath) { - try { - return fs.readFileSync(buildInfoPath, "utf8"); - } catch (err) { - if (err.code === 'ENOENT') { - return "UNKNOWN_" + utility.toISODateString(new Date()); - } else { - logger.error("Unexpected Error!", err); - } - } -} - function sanitizeConfig(config) { let clonedConfig = { ...config }; clonedConfig.accessToken = "******" @@ -76,67 +62,9 @@ function create(configFile) { let config = require(configFile); logger.info("Running with config:"); logger.debug("%o", sanitizeConfig(config)); - let buildInfo = getBuildInfo("../build.info") + let buildInfo = utility.getBuildInfo("../build.info") logger.info("Running version: %s", buildInfo); return new Bot(config, buildInfo, modules); } -function init(bot) { - logger.info("Initializing modules"); - bot.initModules(); - - logger.info("Creating Matrix Client") - bot.client = sdk.createClient({ - baseUrl: bot.config.baseUrl, - accessToken: bot.config.accessToken, - userId: bot.config.userId - }); - - bot.client.on("sync", (state, previousState, data) => { - switch (state) { - case "PREPARED": - bot.connected = true; - bot.sendStatusStartup(); - break; - } - }); - - bot.client.on("RoomMember.membership", (event, member) => { - if (member.membership === "invite" - && bot.config.admin.indexOf(ember.userId) >= 0) { - bot.client.joinRoom(member.roomId).done(() => { - logger.info("Auto-joined %s", member.roomId); - }); - } - }); - - /* Capture Exit Conditions */ - - ["SIGINT", "SIGTERM"].forEach((signature) => { - process.on(signature, async () => { - await bot.sendStatusShutdown() - .then(() => { - logger.info("Gracefully stopping Matrix SDK Client") - bot.client.stopClient(); - }); - process.exit(0); - }); - }); - - process.on('exit', () => { - logger.info("Shutting Down"); - }); - - return bot; -} - -function run(bot) { - // logger.info("Initializing Crypto"); - // await bot.client.initCrypto(); - logger.info("Starting Matrix SDK Client"); - bot.client.startClient(); -} - -exports.create = create; -exports.init = init; -exports.run = run; \ No newline at end of file +exports.create = create; \ No newline at end of file diff --git a/bot/engine.js b/bot/engine.js new file mode 100644 index 0000000..77aa532 --- /dev/null +++ b/bot/engine.js @@ -0,0 +1,61 @@ +let sdk = require('matrix-js-sdk'); +let { logger } = require('./logging'); + +function init(bot) { + logger.info("Initializing modules"); + bot.initModules(); + + logger.info("Creating Matrix Client") + bot.client = sdk.createClient({ + baseUrl: bot.config.baseUrl, + accessToken: bot.config.accessToken, + userId: bot.config.userId + }); + + bot.client.on("sync", (state, previousState, data) => { + switch (state) { + case "PREPARED": + bot.connected = true; + bot.sendStatusStartup(); + break; + } + }); + + bot.client.on("RoomMember.membership", (event, member) => { + if (member.membership === "invite" + && bot.config.admin.indexOf(ember.userId) >= 0) { + bot.client.joinRoom(member.roomId).done(() => { + logger.info("Auto-joined %s", member.roomId); + }); + } + }); + + /* Capture Exit Conditions */ + + ["SIGINT", "SIGTERM"].forEach((signature) => { + process.on(signature, async () => { + await bot.sendStatusShutdown() + .then(() => { + logger.info("Gracefully stopping Matrix SDK Client") + bot.client.stopClient(); + }); + process.exit(0); + }); + }); + + process.on('exit', () => { + logger.info("Shutting Down"); + }); + + return bot; +} + +function run(bot) { + // logger.info("Initializing Crypto"); + // await bot.client.initCrypto(); + logger.info("Starting Matrix SDK Client"); + bot.client.startClient(); +} + +exports.init = init; +exports.run = run; \ No newline at end of file diff --git a/bot/utility.js b/bot/utility.js index 318467c..d1cb9a2 100644 --- a/bot/utility.js +++ b/bot/utility.js @@ -1,3 +1,6 @@ +let fs = require('fs'); +let { logger } = require('./logging'); + function toISODateString(d) { function pad(n) { return n < 10 ? '0' + n : n } return d.getUTCFullYear() + '-' @@ -8,11 +11,24 @@ function toISODateString(d) { + pad(d.getUTCSeconds()) + 'Z' } -function sleep(ms){ - return new Promise(resolve=>{ - setTimeout(resolve,ms) +function getBuildInfo(buildInfoPath) { + try { + return fs.readFileSync(buildInfoPath, "utf8"); + } catch (err) { + if (err.code === 'ENOENT') { + return "UNKNOWN_" + toISODateString(new Date()); + } else { + logger.error("Unexpected Error!", err); + } + } +} + +function sleep(ms) { + return new Promise(resolve => { + setTimeout(resolve, ms) }) } exports.toISODateString = toISODateString; +exports.getBuildInfo = getBuildInfo; exports.sleep = sleep; \ No newline at end of file diff --git a/index.js b/index.js index 51fb774..f4b630b 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ -let bot = require('./bot/bot.js'); -bot.run( - bot.init( +let bot = require('./bot/bot'); +let engine = require('./bot/engine') +engine.run( + engine.init( bot.create("../data/config.json") ) ); From f866db1fad844197d255e02ce1b0ee0e673a9906 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 15:05:43 -0600 Subject: [PATCH 07/26] Additional code cleanup for engine and bot classes --- bot/bot.js | 19 +++++---- bot/engine.js | 112 ++++++++++++++++++++++++++++---------------------- index.js | 9 ++-- 3 files changed, 78 insertions(+), 62 deletions(-) diff --git a/bot/bot.js b/bot/bot.js index 244d4db..f78aeec 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -1,19 +1,24 @@ +let sdk = require('matrix-js-sdk'); let message = require('./message'); let utility = require('./utility'); let { logger } = require('./logging'); -let { modules } = require('./module/index') class Bot { - constructor(config, buildInfo, modules) { + constructor(config, buildInfo) { this.config = config; this.buildInfo = buildInfo; this.connected = false; - this.modules = modules; } - initModules() { - modules.forEach((module) => { - logger.info("Loading module: %s", module.name); + /** + * Initialize the bot connection + */ + init() { + logger.info("Creating Matrix Client") + this.client = sdk.createClient({ + baseUrl: this.config.baseUrl, + accessToken: this.config.accessToken, + userId: this.config.userId }); } @@ -64,7 +69,7 @@ function create(configFile) { logger.debug("%o", sanitizeConfig(config)); let buildInfo = utility.getBuildInfo("../build.info") logger.info("Running version: %s", buildInfo); - return new Bot(config, buildInfo, modules); + return new Bot(config, buildInfo); } exports.create = create; \ No newline at end of file diff --git a/bot/engine.js b/bot/engine.js index 77aa532..b206052 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -1,61 +1,73 @@ -let sdk = require('matrix-js-sdk'); let { logger } = require('./logging'); +let { modules } = require('./module/index') -function init(bot) { - logger.info("Initializing modules"); - bot.initModules(); +class Engine { + constructor(bot, modules) { + this.bot = bot; + this.modules = modules; + } - logger.info("Creating Matrix Client") - bot.client = sdk.createClient({ - baseUrl: bot.config.baseUrl, - accessToken: bot.config.accessToken, - userId: bot.config.userId - }); - - bot.client.on("sync", (state, previousState, data) => { - switch (state) { - case "PREPARED": - bot.connected = true; - bot.sendStatusStartup(); - break; - } - }); - - bot.client.on("RoomMember.membership", (event, member) => { - if (member.membership === "invite" - && bot.config.admin.indexOf(ember.userId) >= 0) { - bot.client.joinRoom(member.roomId).done(() => { - logger.info("Auto-joined %s", member.roomId); - }); - } - }); - - /* Capture Exit Conditions */ - - ["SIGINT", "SIGTERM"].forEach((signature) => { - process.on(signature, async () => { - await bot.sendStatusShutdown() - .then(() => { - logger.info("Gracefully stopping Matrix SDK Client") - bot.client.stopClient(); + init() { + logger.info("Initializing modules"); + this.initModules(); + + this.bot.init(); + + this.bot.client.on("sync", (state, previousState, data) => { + switch (state) { + case "PREPARED": + this.bot.connected = true; + this.bot.sendStatusStartup(); + break; + } + }); + + this.bot.client.on("RoomMember.membership", (event, member) => { + if (member.membership === "invite" + && this.bot.config.admin.indexOf(ember.userId) >= 0) { + this.bot.client.joinRoom(member.roomId).done(() => { + logger.info("Auto-joined %s", member.roomId); }); - process.exit(0); + } + }); + + /* Capture Exit Conditions */ + + ["SIGINT", "SIGTERM"].forEach((signature) => { + process.on(signature, async () => { + await this.bot.sendStatusShutdown() + .then(() => { + logger.info("Gracefully stopping Matrix SDK Client") + this.bot.client.stopClient(); + }); + process.exit(0); + }); }); - }); + + process.on('exit', () => { + logger.info("Shutting Down"); + }); + + return this; + } - process.on('exit', () => { - logger.info("Shutting Down"); - }); + initModules() { + this.modules.forEach((module) => { + logger.info("Loading module: %s", module.name); + }); + } - return bot; + run() { + // logger.info("Initializing Crypto"); + // await bot.client.initCrypto(); + logger.info("Starting Matrix SDK Client"); + this.bot.client.startClient(); + return this; + } } -function run(bot) { - // logger.info("Initializing Crypto"); - // await bot.client.initCrypto(); - logger.info("Starting Matrix SDK Client"); - bot.client.startClient(); +function create(bot) { + return new Engine(bot, modules) } -exports.init = init; -exports.run = run; \ No newline at end of file +exports.create = create; \ No newline at end of file diff --git a/index.js b/index.js index f4b630b..f03dfd1 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,6 @@ let bot = require('./bot/bot'); let engine = require('./bot/engine') -engine.run( - engine.init( - bot.create("../data/config.json") - ) -); + +engine.create( + bot.create("../data/config.json") +).init().run(); From 08ea447adde4a78e5350f527a8774d5320de1f65 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 15:23:30 -0600 Subject: [PATCH 08/26] Functionality improvements and code cleanup * Added message handling stub * Cleaned up client init and config * Added timestamp to logging --- bot/bot.js | 37 ++++++++++++++++++++++++++++++++++++- bot/engine.js | 41 ++++++++++++++--------------------------- bot/logging.js | 1 + 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/bot/bot.js b/bot/bot.js index f78aeec..437d8a2 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -13,13 +13,48 @@ class Bot { /** * Initialize the bot connection */ - init() { + init(messageCallback) { logger.info("Creating Matrix Client") this.client = sdk.createClient({ baseUrl: this.config.baseUrl, accessToken: this.config.accessToken, userId: this.config.userId }); + + this.client.on("sync", (state, previousState, data) => { + switch (state) { + case "PREPARED": + this.connected = true; + this.sendStatusStartup(); + break; + case "SYNCING": + logger.debug("Syncing") + break; + default: + logger.error("Unexpected sync state: %s", state); + process.exit(1); + } + }); + + this.client.on("RoomMember.membership", (event, member) => { + if (member.membership === "invite" + && this.config.admin.indexOf(ember.userId) >= 0) { + this.client.joinRoom(member.roomId).done(() => { + logger.info("Auto-joined %s", member.roomId); + }); + } + }); + + this.client.on("Room.timeline", messageCallback); + + return this; + } + + connect() { + // logger.info("Initializing Crypto"); + // await bot.client.initCrypto(); + logger.info("Starting Matrix SDK Client"); + this.client.startClient(); } sendStatusStartup() { diff --git a/bot/engine.js b/bot/engine.js index b206052..a55fb84 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -10,29 +10,19 @@ class Engine { init() { logger.info("Initializing modules"); this.initModules(); - - this.bot.init(); - - this.bot.client.on("sync", (state, previousState, data) => { - switch (state) { - case "PREPARED": - this.bot.connected = true; - this.bot.sendStatusStartup(); - break; - } - }); - - this.bot.client.on("RoomMember.membership", (event, member) => { - if (member.membership === "invite" - && this.bot.config.admin.indexOf(ember.userId) >= 0) { - this.bot.client.joinRoom(member.roomId).done(() => { - logger.info("Auto-joined %s", member.roomId); - }); + + /* Bind Message Parsing */ + let handleMessages = function (event, room, toStartOfTimeline) { + if (event.getType() !== "m.room.message") { + return; // only use messages } - }); - + logger.debug("[%s] %s", room.name, event.event.content.body); + } + + this.bot.init(handleMessages); + /* Capture Exit Conditions */ - + ["SIGINT", "SIGTERM"].forEach((signature) => { process.on(signature, async () => { await this.bot.sendStatusShutdown() @@ -43,11 +33,11 @@ class Engine { process.exit(0); }); }); - + process.on('exit', () => { logger.info("Shutting Down"); }); - + return this; } @@ -58,10 +48,7 @@ class Engine { } run() { - // logger.info("Initializing Crypto"); - // await bot.client.initCrypto(); - logger.info("Starting Matrix SDK Client"); - this.bot.client.startClient(); + this.bot.connect(); return this; } } diff --git a/bot/logging.js b/bot/logging.js index 0c5c87b..2b0a863 100644 --- a/bot/logging.js +++ b/bot/logging.js @@ -3,6 +3,7 @@ let winston = require('winston'); let logger = winston.createLogger({ level: 'info', format: winston.format.combine( + winston.format.timestamp(), winston.format.splat(), winston.format.json() ), From 41a63558fc55add610935bb009131f916fe9fae3 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 15:40:21 -0600 Subject: [PATCH 09/26] Moved config into a seperate module --- .dockerignore | 2 +- .gitignore | 2 +- bot/bot.js | 11 +---------- bot/config.js | 22 ++++++++++++++++++++++ bot/engine.js | 8 +++++--- index.js | 8 ++++++-- 6 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 bot/config.js diff --git a/.dockerignore b/.dockerignore index 0b2e08c..ba561b3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,2 @@ -data/config.json +data/*config.json log/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index cf70976..bc3e975 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ node_modules/ # Config files -data/config.json +data/*config.json # Log files log/ diff --git a/bot/bot.js b/bot/bot.js index 437d8a2..37a1d92 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -92,16 +92,7 @@ class Bot { } } -function sanitizeConfig(config) { - let clonedConfig = { ...config }; - clonedConfig.accessToken = "******" - return clonedConfig; -} - -function create(configFile) { - let config = require(configFile); - logger.info("Running with config:"); - logger.debug("%o", sanitizeConfig(config)); +function create(config) { let buildInfo = utility.getBuildInfo("../build.info") logger.info("Running version: %s", buildInfo); return new Bot(config, buildInfo); diff --git a/bot/config.js b/bot/config.js new file mode 100644 index 0000000..ad6fb89 --- /dev/null +++ b/bot/config.js @@ -0,0 +1,22 @@ +let { logger } = require('./logging'); + +var configPath = null; +var config = null; + +function sanitizeConfig(config) { + let clonedConfig = { ...config }; + clonedConfig.accessToken = "******" + return clonedConfig; +} + +function getConfig(configFile) { + if (config === null) { + configPath = configFile; + config = require(configFile); + logger.info("Leaded config:"); + logger.debug("%o", sanitizeConfig(config)); + } + return config; +} + +exports.getConfig = getConfig \ No newline at end of file diff --git a/bot/engine.js b/bot/engine.js index a55fb84..3627dff 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -2,7 +2,8 @@ let { logger } = require('./logging'); let { modules } = require('./module/index') class Engine { - constructor(bot, modules) { + constructor(config, bot, modules) { + this.config = config this.bot = bot; this.modules = modules; } @@ -14,6 +15,7 @@ class Engine { /* Bind Message Parsing */ let handleMessages = function (event, room, toStartOfTimeline) { if (event.getType() !== "m.room.message") { + logger.debug("Recieved message of type: %s", event.getType()); return; // only use messages } logger.debug("[%s] %s", room.name, event.event.content.body); @@ -53,8 +55,8 @@ class Engine { } } -function create(bot) { - return new Engine(bot, modules) +function create(config, bot) { + return new Engine(config, bot, modules) } exports.create = create; \ No newline at end of file diff --git a/index.js b/index.js index f03dfd1..56f2eaa 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,10 @@ let bot = require('./bot/bot'); -let engine = require('./bot/engine') +let { getConfig } = require('./bot/config'); +let engine = require('./bot/engine'); + +let config = getConfig("../data/config.json") engine.create( - bot.create("../data/config.json") + config, + bot.create(config) ).init().run(); From 7e03d89decea59273d4e378073807bf775c38f80 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 15:58:07 -0600 Subject: [PATCH 10/26] Improving module support * Added AbstractModule * Added giphy module * Updated Admin module --- bot/engine.js | 5 +++-- bot/module/abstract.js | 24 ++++++++++++++++++++++++ bot/module/admin.js | 20 ++++++++++++-------- bot/module/giphy.js | 17 +++++++++++++++++ bot/module/index.js | 8 ++++++-- 5 files changed, 62 insertions(+), 12 deletions(-) create mode 100644 bot/module/abstract.js create mode 100644 bot/module/giphy.js diff --git a/bot/engine.js b/bot/engine.js index 3627dff..34c848d 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -13,12 +13,13 @@ class Engine { this.initModules(); /* Bind Message Parsing */ - let handleMessages = function (event, room, toStartOfTimeline) { + let handleMessages = (event, room, toStartOfTimeline) => { if (event.getType() !== "m.room.message") { logger.debug("Recieved message of type: %s", event.getType()); return; // only use messages + } else { + logger.debug("[%s] %s", room.name, event.event.content.body); } - logger.debug("[%s] %s", room.name, event.event.content.body); } this.bot.init(handleMessages); diff --git a/bot/module/abstract.js b/bot/module/abstract.js new file mode 100644 index 0000000..5a1441d --- /dev/null +++ b/bot/module/abstract.js @@ -0,0 +1,24 @@ +/** + * Base module that all modules extend + */ + +let { logger } = require('../logging'); + +class AbstractModule { + name = "AbstractModule" + description = "Base Module That All Other Modules Extend" + command = "abstract_module" + + constructor(name, description, command) { + this.name = name; + this.description = description; + this.command = command; + } + + handleMessage(event, room) { + logger.debug("[%s] [%s] [%s]", this.name, room.name, event.event.content.body); + } + +} + +exports.AbstractModule = AbstractModule \ No newline at end of file diff --git a/bot/module/admin.js b/bot/module/admin.js index 729e000..08dff6b 100644 --- a/bot/module/admin.js +++ b/bot/module/admin.js @@ -2,12 +2,16 @@ * Administration module */ - class AdminModule { - constructor() { - this.name = "Administration" - this.description = "Support administration tasks" - this.command = "admin" - } - } +let { AbstractModule } = require('./abstract'); - exports.module = new AdminModule(); \ No newline at end of file +class AdminModule extends AbstractModule { + constructor() { + super( + "Administration", + "Support administration tasks", + "admin" + ); + } +} + +exports.module = new AdminModule(); \ No newline at end of file diff --git a/bot/module/giphy.js b/bot/module/giphy.js new file mode 100644 index 0000000..ab48fb6 --- /dev/null +++ b/bot/module/giphy.js @@ -0,0 +1,17 @@ +/** + * Giphy module + */ + +let { AbstractModule } = require('./abstract'); + +class GiphyModule extends AbstractModule { + constructor() { + super( + "Giphy", + "Insert Giphy Links/Media", + "giphy" + ); + } +} + +exports.module = new GiphyModule(); \ No newline at end of file diff --git a/bot/module/index.js b/bot/module/index.js index 1c36a30..cd1220f 100644 --- a/bot/module/index.js +++ b/bot/module/index.js @@ -2,10 +2,14 @@ * Manage the registered modules */ - let adminModule = require('./admin'); + let admin = require('./admin'); + let giphy = require('./giphy') function getModules() { - return [adminModule.module]; + return [ + admin.module, + giphy.module + ]; } exports.modules = getModules(); \ No newline at end of file From eb2f2a006bc19dab3c53659a208e7cf2d3d059f9 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 18:19:33 -0600 Subject: [PATCH 11/26] Adding help message support --- bot/engine.js | 74 ++++++++++++++++++++++++++++++++++-------- bot/module/abstract.js | 6 ++++ bot/module/giphy.js | 4 +-- bot/module/help.js | 37 +++++++++++++++++++++ bot/utility.js | 23 ++++++++++++- package-lock.json | 5 +++ package.json | 1 + 7 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 bot/module/help.js diff --git a/bot/engine.js b/bot/engine.js index 34c848d..d5f879f 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -1,11 +1,41 @@ let { logger } = require('./logging'); -let { modules } = require('./module/index') +let { modules } = require('./module/index'); +let trie = require('trie-prefix-tree'); +let { getShortestPrefix } = require('./utility'); +let help = require('./module/help'); +let message = require('./message'); +let utility = require('./utility'); + +let sentinelValue = '!'; class Engine { constructor(config, bot, modules) { - this.config = config + this.config = config; this.bot = bot; this.modules = modules; + this.moduleMap = new Map(); + this.commandMap = new Map(); + this.commandRadixTree = trie([]); + } + + initModules() { + this.modules.forEach((module) => { + logger.info("Loading module: %s", module.name); + this.moduleMap.set(module.command, module); + this.commandMap.set(module.command, module); + this.commandRadixTree.addWord(module.command); + }); + + this.modules.forEach((module) => { + let shortCommand = getShortestPrefix(this.commandRadixTree, module.command, 3); + logger.info("Adding short command %s for module: %s", shortCommand, module.name); + this.commandMap.set(shortCommand, module); + }); + + this.helpModule = help.create(this.moduleMap) + this.commandMap.set('help', this.helpModule); + + logger.info("Bound modules to keywords: %o", this.moduleMap); } init() { @@ -13,12 +43,36 @@ class Engine { this.initModules(); /* Bind Message Parsing */ + let engine = this; let handleMessages = (event, room, toStartOfTimeline) => { - if (event.getType() !== "m.room.message") { - logger.debug("Recieved message of type: %s", event.getType()); - return; // only use messages - } else { - logger.debug("[%s] %s", room.name, event.event.content.body); + /* Don't process messages from self */ + if (event.sender.userId !== this.config.userId) { + /* don't process messages that aren't of type m.room.message */ + if (event.getType() !== "m.room.message") { + logger.debug("Recieved message of type: %s", event.getType()); + return; + } else { + let messageBody = event.event.content.body; + logger.debug("[%s] %s", room.name, messageBody); + if (messageBody.indexOf(sentinelValue) === 0) { + let command = messageBody.split(' ')[0].substring(1); + var responseMessage = null; + if (engine.commandMap.has(command)) { + responseMessage = engine.commandMap.get(command).handleMessage(event, room); + } else { + responseMessage = engine.helpModule.help(); + } + + logger.debug("Responding to room: %s", room.roomId); + if (responseMessage instanceof Object) { + engine.bot.client.sendMessage(room.roomId, responseMessage); + } else if (utility.isString(responseMessage)) { + engine.bot.client.sendMessage(room.roomId, message.createBasic(responseMessage)); + } else { + logger.error("Unable to process response message: %s", responseMessage); + } + } + } } } @@ -44,12 +98,6 @@ class Engine { return this; } - initModules() { - this.modules.forEach((module) => { - logger.info("Loading module: %s", module.name); - }); - } - run() { this.bot.connect(); return this; diff --git a/bot/module/abstract.js b/bot/module/abstract.js index 5a1441d..91233f7 100644 --- a/bot/module/abstract.js +++ b/bot/module/abstract.js @@ -3,6 +3,7 @@ */ let { logger } = require('../logging'); +let message = require('../message'); class AbstractModule { name = "AbstractModule" @@ -17,6 +18,11 @@ class AbstractModule { handleMessage(event, room) { logger.debug("[%s] [%s] [%s]", this.name, room.name, event.event.content.body); + return message.createBasic(this.name + " processed the message"); + } + + help(event, room) { + return message.createBasic(this.name + " HELP!"); } } diff --git a/bot/module/giphy.js b/bot/module/giphy.js index ab48fb6..b0a9698 100644 --- a/bot/module/giphy.js +++ b/bot/module/giphy.js @@ -7,8 +7,8 @@ let { AbstractModule } = require('./abstract'); class GiphyModule extends AbstractModule { constructor() { super( - "Giphy", - "Insert Giphy Links/Media", + "Giphy", + "Insert Giphy Links/Media", "giphy" ); } diff --git a/bot/module/help.js b/bot/module/help.js new file mode 100644 index 0000000..9816ff4 --- /dev/null +++ b/bot/module/help.js @@ -0,0 +1,37 @@ +/** + * Help module + */ + +let { AbstractModule } = require('./abstract'); +let { logger } = require('../logging'); + +class HelpModule extends AbstractModule { + constructor(commandMap) { + super( + "Help", + "Provide helpful information about other modules.", + "help" + ); + this.commandMap = commandMap; + } + + addCommand(command, module) { + logger.debug("Adding help for command %s against module %s", command, module.name); + this.commands.push(command); + } + + help() { + let help = `!help `; + for (let command of Array.from(this.commandMap.keys()).sort()) { + help += "\n!help " + command + " : " + this.commandMap.get(command).description; + } + return help; + } +} + +function create(commandMap) { + return new HelpModule(commandMap); +} + +exports.create = create; +exports.module = new HelpModule(); \ No newline at end of file diff --git a/bot/utility.js b/bot/utility.js index d1cb9a2..67ddeb9 100644 --- a/bot/utility.js +++ b/bot/utility.js @@ -1,6 +1,21 @@ let fs = require('fs'); let { logger } = require('./logging'); +function getShortestPrefix(radixTree, key, sliceSize) { + let shortKey = key.substring(0, sliceSize); + let keyCount = radixTree.countPrefix(shortKey); + if (keyCount < 1) { + return null; + } + if (key.length === sliceSize && radixTree.getPrefix(shortKey).includes(key)) { + return null; + } + if (keyCount === 1) { + return shortKey; + } + return getShortestPrefix(radixTree, key, sliceSize + 1); +} + function toISODateString(d) { function pad(n) { return n < 10 ? '0' + n : n } return d.getUTCFullYear() + '-' @@ -29,6 +44,12 @@ function sleep(ms) { }) } +function isString(s) { + return typeof(s) === 'string' || s instanceof String; +} + +exports.getShortestPrefix = getShortestPrefix; exports.toISODateString = toISODateString; exports.getBuildInfo = getBuildInfo; -exports.sleep = sleep; \ No newline at end of file +exports.sleep = sleep; +exports.isString = isString; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 16e15f0..3765bb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1177,6 +1177,11 @@ } } }, + "trie-prefix-tree": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/trie-prefix-tree/-/trie-prefix-tree-1.5.1.tgz", + "integrity": "sha512-Jjvj/dA97wXnabG/NLJUgo4IQMj6vucH+Qxm7of/omfWSmZlPqdRU6Ta4GmQqCZH+n3/iYZUwfvUoEhB0Hs83Q==" + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", diff --git a/package.json b/package.json index d3d071f..1c09411 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "matrix-js-sdk": "2.4.5", + "trie-prefix-tree": "^1.5.1", "winston": "^3.2.1" }, "devDependencies": { From cdac7609fd7f102cd65774ed93b28572dbaa409b Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 18:30:39 -0600 Subject: [PATCH 12/26] Handle RECONNECTING and reduce initial sync --- bot/bot.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/bot.js b/bot/bot.js index 37a1d92..20d0707 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -30,6 +30,9 @@ class Bot { case "SYNCING": logger.debug("Syncing") break; + case "RECONNECTING": + logger.debug("Reconnecting"); + break; default: logger.error("Unexpected sync state: %s", state); process.exit(1); @@ -50,11 +53,13 @@ class Bot { return this; } - connect() { + async connect() { // logger.info("Initializing Crypto"); // await bot.client.initCrypto(); logger.info("Starting Matrix SDK Client"); - this.client.startClient(); + await this.client.startClient({ + initialSyncLimit: 0 + }); } sendStatusStartup() { From 4cce06e2db81fae29baf4093913c855512ac6582 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Sun, 29 Dec 2019 20:15:13 -0600 Subject: [PATCH 13/26] Making module respones a callback * Moved responding code into a callback * Fixed the broken LOG_LEVEL setup * Spelling corrections * Printing rooms joined to logs on startup --- bot/bot.js | 16 ++++++++------ bot/config.js | 2 +- bot/engine.js | 48 +++++++++++++++++++++++++++++++----------- bot/logging.js | 3 ++- bot/module/abstract.js | 7 ++++-- 5 files changed, 53 insertions(+), 23 deletions(-) diff --git a/bot/bot.js b/bot/bot.js index 20d0707..55ab2c0 100644 --- a/bot/bot.js +++ b/bot/bot.js @@ -21,11 +21,15 @@ class Bot { userId: this.config.userId }); - this.client.on("sync", (state, previousState, data) => { + this.client.on("sync", async (state, previousState, data) => { switch (state) { case "PREPARED": this.connected = true; - this.sendStatusStartup(); + await this.sendStatusStartup(); + this.client.getJoinedRooms() + .done((rooms) => { + logger.info("Connected to: %o", rooms) + }); break; case "SYNCING": logger.debug("Syncing") @@ -38,11 +42,11 @@ class Bot { process.exit(1); } }); - + this.client.on("RoomMember.membership", (event, member) => { if (member.membership === "invite" && this.config.admin.indexOf(ember.userId) >= 0) { - this.client.joinRoom(member.roomId).done(() => { + this.client.joinRoom(member.roomId).done(() => { logger.info("Auto-joined %s", member.roomId); }); } @@ -57,9 +61,7 @@ class Bot { // logger.info("Initializing Crypto"); // await bot.client.initCrypto(); logger.info("Starting Matrix SDK Client"); - await this.client.startClient({ - initialSyncLimit: 0 - }); + await this.client.startClient(); } sendStatusStartup() { diff --git a/bot/config.js b/bot/config.js index ad6fb89..9a00593 100644 --- a/bot/config.js +++ b/bot/config.js @@ -13,7 +13,7 @@ function getConfig(configFile) { if (config === null) { configPath = configFile; config = require(configFile); - logger.info("Leaded config:"); + logger.info("Loaded config:"); logger.debug("%o", sanitizeConfig(config)); } return config; diff --git a/bot/engine.js b/bot/engine.js index d5f879f..f4a6958 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -14,6 +14,7 @@ class Engine { this.bot = bot; this.modules = modules; this.moduleMap = new Map(); + this.commands = []; this.commandMap = new Map(); this.commandRadixTree = trie([]); } @@ -34,6 +35,7 @@ class Engine { this.helpModule = help.create(this.moduleMap) this.commandMap.set('help', this.helpModule); + this.commands = Array.from(this.commandMap.keys()).sort() logger.info("Bound modules to keywords: %o", this.moduleMap); } @@ -56,20 +58,13 @@ class Engine { logger.debug("[%s] %s", room.name, messageBody); if (messageBody.indexOf(sentinelValue) === 0) { let command = messageBody.split(' ')[0].substring(1); - var responseMessage = null; if (engine.commandMap.has(command)) { - responseMessage = engine.commandMap.get(command).handleMessage(event, room); + engine.commandMap.get(command).handleMessage(event, room, sendResponseMessageCallback(engine.bot)); } else { - responseMessage = engine.helpModule.help(); - } - - logger.debug("Responding to room: %s", room.roomId); - if (responseMessage instanceof Object) { - engine.bot.client.sendMessage(room.roomId, responseMessage); - } else if (utility.isString(responseMessage)) { - engine.bot.client.sendMessage(room.roomId, message.createBasic(responseMessage)); - } else { - logger.error("Unable to process response message: %s", responseMessage); + let responseMessage = "The following commands are recognized" + responseMessage += "\n" + engine.commands.join(", ") + responseMessage += "\nAdditional information can be discovered with !help " + sendResponseMessage(engine.bot, room, responseMessage); } } } @@ -104,6 +99,35 @@ class Engine { } } +/** + * Handle the callback sending messages via the bot + * + * @param {*} bot + * @param {*} room + * @param {*} responseMessage + */ +function sendResponseMessage(bot, room, responseMessage) { + logger.debug("Responding to room: %s", room.roomId); + if (responseMessage instanceof Object) { + bot.client.sendMessage(room.roomId, responseMessage); + } else if (utility.isString(responseMessage)) { + bot.client.sendMessage(room.roomId, message.createBasic(responseMessage)); + } else { + logger.error("Unable to process response message: %s", responseMessage); + } +} + +/** + * Wrapper to produce a callback function that can be passed to the modules + * + * @param {*} bot + */ +function sendResponseMessageCallback(bot) { + return (room, responseMessage) => { + sendResponseMessage(bot, room, responseMessage); + } +} + function create(config, bot) { return new Engine(config, bot, modules) } diff --git a/bot/logging.js b/bot/logging.js index 2b0a863..ca3eea6 100644 --- a/bot/logging.js +++ b/bot/logging.js @@ -22,7 +22,8 @@ if (process.env.NODE_ENV !== 'production') { })); } -if (process.env.LOG_LEVEL !== '') { +if ('LOG_LEVE' in process.env) { + logger.info('LOG_LEVEL:', process.env.LOG_LEVEL) logger.level = process.env.LOG_LEVEL } diff --git a/bot/module/abstract.js b/bot/module/abstract.js index 91233f7..e98fba4 100644 --- a/bot/module/abstract.js +++ b/bot/module/abstract.js @@ -16,9 +16,12 @@ class AbstractModule { this.command = command; } - handleMessage(event, room) { + handleMessage(event, room, callback) { logger.debug("[%s] [%s] [%s]", this.name, room.name, event.event.content.body); - return message.createBasic(this.name + " processed the message"); + callback( + room, + message.createBasic(this.name + " processed the message") + ); } help(event, room) { From 06cf7aa19f3f0564c334b705818025b498200188 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 00:04:42 -0600 Subject: [PATCH 14/26] Working delegation to defaultCommand * Completed basic help module * Added meta binding for module commands * Fixed LOG_LEVEL env reader * Added isFunnction utility * Aded variable depth getObjectKeysToPrototype * Added basic message handlers in AbstractModule --- bot/engine.js | 27 ++++++---- bot/logging.js | 2 +- bot/module/abstract.js | 114 ++++++++++++++++++++++++++++++++++++++--- bot/module/help.js | 35 +++++++++---- bot/utility.js | 34 +++++++++++- 5 files changed, 183 insertions(+), 29 deletions(-) diff --git a/bot/engine.js b/bot/engine.js index f4a6958..195bbf8 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -5,6 +5,7 @@ let { getShortestPrefix } = require('./utility'); let help = require('./module/help'); let message = require('./message'); let utility = require('./utility'); +let { initModule } = require('./module/abstract'); let sentinelValue = '!'; @@ -20,20 +21,28 @@ class Engine { } initModules() { - this.modules.forEach((module) => { - logger.info("Loading module: %s", module.name); - this.moduleMap.set(module.command, module); - this.commandMap.set(module.command, module); - this.commandRadixTree.addWord(module.command); + this.modules.forEach((mod) => { + logger.info("Loading module: %s", mod.name); + initModule(mod); + console.log("Recognized commands: %s", mod.getRecognizedCommands()) + this.moduleMap.set(mod.command, mod); + this.commandMap.set(mod.command, mod); + this.commandRadixTree.addWord(mod.command); }); - this.modules.forEach((module) => { - let shortCommand = getShortestPrefix(this.commandRadixTree, module.command, 3); - logger.info("Adding short command %s for module: %s", shortCommand, module.name); - this.commandMap.set(shortCommand, module); + this.modules.forEach((mod) => { + let shortCharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 1); + let short3CharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 3); + let shortCommandAliases = [shortCharCommand, short3CharCommand]; + logger.info("Adding short command %s for module: %s", shortCommandAliases, mod.name); + shortCommandAliases.forEach( (commandAlias) => { + this.commandMap.set(commandAlias, mod); + }) }); this.helpModule = help.create(this.moduleMap) + initModule(this.helpModule); + this.moduleMap.set(this.helpModule.command, this.helpModule); this.commandMap.set('help', this.helpModule); this.commands = Array.from(this.commandMap.keys()).sort() diff --git a/bot/logging.js b/bot/logging.js index ca3eea6..06e43ac 100644 --- a/bot/logging.js +++ b/bot/logging.js @@ -22,7 +22,7 @@ if (process.env.NODE_ENV !== 'production') { })); } -if ('LOG_LEVE' in process.env) { +if ('LOG_LEVEL' in process.env) { logger.info('LOG_LEVEL:', process.env.LOG_LEVEL) logger.level = process.env.LOG_LEVEL } diff --git a/bot/module/abstract.js b/bot/module/abstract.js index e98fba4..afb3187 100644 --- a/bot/module/abstract.js +++ b/bot/module/abstract.js @@ -4,30 +4,132 @@ let { logger } = require('../logging'); let message = require('../message'); +let { isFunction, getObjectKeysToPrototype } = require('../utility'); class AbstractModule { - name = "AbstractModule" - description = "Base Module That All Other Modules Extend" - command = "abstract_module" + /* + Name of the module used in help documentation and logging. + */ + name = "AbstractModule"; + + /* + Short description of the module functionality. + */ + description = "Base Module That All Other Modules Extend"; + + /* + The exported command used to invoke the module directly. + */ + command = "abstract_module"; + + /* + The default method to call when a command word is not recognized. + */ + defaultCommand = null; + /* + The module should be hidden from help and command dialogs. + */ + hidden = false; + /* + This module should receive all messages, regardless of whether + the module was directly referenced with a command. + */ + canHandleIndirectMessages = false; constructor(name, description, command) { this.name = name; this.description = description; this.command = command; + this._recognizedCommands = new Map(); + } + + addRecognizedCommand(command, methodName) { + this._recognizedCommands.set(command, methodName) + } + + getRecognizedCommands() { + return this._recognizedCommands.keys(); } + /** + * Default functionality for receiving and processing a message. + * + * Override this if the module needs to do more complicated message processing. + */ handleMessage(event, room, callback) { 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("Attempting to call %s with %s", command, args); + let responseMessage = this.processMessage(command, args); + callback( room, - message.createBasic(this.name + " processed the message") + responseMessage ); } - help(event, room) { + /* + Call the command method with the args + */ + processMessage(command, ...args) { + if (command in this._recognizedCommands) { + logger.debug("Calling %s with %s", this._recognizedCommands.get(command), args); + return this[this._recognizedCommands.get(command)](...args); + } else { + if (this.defaultCommand != null) { + logger.debug("Attempting to use default command %s", this.defaultCommand); + try { + let newArgs = [command].concat(...args); + logger.debug("Calling %s with %s", this._recognizedCommands.get(this.defaultCommand), newArgs); + return this[this._recognizedCommands.get(this.defaultCommand)](...newArgs); + } catch (e) { + logger.error("Error while calling default command %s %s", this.defaultCommand, e); + return this.cmd_help(); + } + } else { + logger.debug("Unrecognized command %s", command); + return this.cmd_help(); + } + } + } + + /* Basic cmd methods */ + + /* + return basic help information,. + */ + cmd_help(...args) { return message.createBasic(this.name + " HELP!"); } +} + +let abstractModulePrototype = Object.getPrototypeOf(new AbstractModule('', '', '')); +/* +Initialization of a module. +*/ +function init(mod) { + logger.debug("Initializing module %s", mod.name) + 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()); } -exports.AbstractModule = AbstractModule \ No newline at end of file +exports.AbstractModule = AbstractModule +exports.initModule = init; \ No newline at end of file diff --git a/bot/module/help.js b/bot/module/help.js index 9816ff4..5685b84 100644 --- a/bot/module/help.js +++ b/bot/module/help.js @@ -12,20 +12,34 @@ class HelpModule extends AbstractModule { "Provide helpful information about other modules.", "help" ); - this.commandMap = commandMap; + this._commandMap = commandMap; + this._commandList = Array.from(commandMap.keys()).sort(); + this.defaultCommand = 'help'; } - addCommand(command, module) { - logger.debug("Adding help for command %s against module %s", command, module.name); - this.commands.push(command); + _default_help_message() { + let help = `!help `; + for (let command of this._commandList) { + help += "\n!help " + command + " : " + this._commandMap.get(command).description; + } + return help; } - help() { - let help = `!help `; - for (let command of Array.from(this.commandMap.keys()).sort()) { - help += "\n!help " + command + " : " + this.commandMap.get(command).description; + cmd_help(...args) { + logger.debug("%o", args) + if (args.length < 1) { + return this._default_help_message(); + } else { + let command = args[0]; + logger.debug("Looking up help for %s from %o", command, this._commandMap); + if (this._commandList.includes(command)) { + return this._commandMap.get(command).cmd_help(); + } else { + let help = command + " is an unrecognized module\n"; + help += this._default_help_message(); + return help; + } } - return help; } } @@ -33,5 +47,4 @@ function create(commandMap) { return new HelpModule(commandMap); } -exports.create = create; -exports.module = new HelpModule(); \ No newline at end of file +exports.create = create; \ No newline at end of file diff --git a/bot/utility.js b/bot/utility.js index 67ddeb9..049dfd2 100644 --- a/bot/utility.js +++ b/bot/utility.js @@ -45,11 +45,41 @@ function sleep(ms) { } function isString(s) { - return typeof(s) === 'string' || s instanceof String; + return typeof (s) === 'string' || s instanceof String; +} + +function isFunction(f) { + return f && {}.toString.call(f) === '[object Function]'; +} + +/** + * Parse the prototype tree to return all accessible properties till + * reaching a sentinelPrototype. + * + * Optionally provide a filtering function to return only the names that match. + * + * @param {*} initialObj The starting object to derive the from + * @param {*} sentinelPrototype The prototype that represents the end of the line + * @param {*} filterFunc A fioltering function for the return names + */ +function getObjectKeysToPrototype(initialObj, sentinelPrototype, filterFunc = (e) => true) { + let prototypeChain = [] + var targetPrototype = initialObj; + while (Object.getPrototypeOf(targetPrototype) && targetPrototype !== sentinelPrototype) { + targetPrototype = Object.getPrototypeOf(targetPrototype); + prototypeChain.push(targetPrototype); + } + // console.log("Prototype chain: %s", prototypeChain); + let completePropertyNames = prototypeChain.map((obj) => { + return Object.getOwnPropertyNames(obj); + }) + return [Object.getOwnPropertyNames(initialObj)].concat.apply([], completePropertyNames).filter(filterFunc); } exports.getShortestPrefix = getShortestPrefix; exports.toISODateString = toISODateString; exports.getBuildInfo = getBuildInfo; exports.sleep = sleep; -exports.isString = isString; \ No newline at end of file +exports.isString = isString; +exports.isFunction = isFunction; +exports.getObjectKeysToPrototype = getObjectKeysToPrototype; \ No newline at end of file From 4c16d8161e9af45eb7ed050830512fa09557cf44 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 00:25:29 -0600 Subject: [PATCH 15/26] Fix broken command lookup * Migrate to helpAndUsage templates for standard help --- bot/module/abstract.js | 28 ++++++++++++++++++---------- bot/module/admin.js | 2 ++ bot/module/giphy.js | 2 ++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/bot/module/abstract.js b/bot/module/abstract.js index afb3187..de16bac 100644 --- a/bot/module/abstract.js +++ b/bot/module/abstract.js @@ -17,6 +17,12 @@ class AbstractModule { */ description = "Base Module That All Other Modules Extend"; + /* + A helpful multiline string that defines the module usage + */ + helpAndUsage = `Example: !abstract_module + !abstract_module : scares people` + /* The exported command used to invoke the module directly. */ @@ -40,15 +46,17 @@ class AbstractModule { this.name = name; this.description = description; this.command = command; - this._recognizedCommands = new Map(); + this._recognizedCommands = []; + this._recognizedCommandMap = new Map(); } addRecognizedCommand(command, methodName) { - this._recognizedCommands.set(command, methodName) + this._recognizedCommands.push(command); + this._recognizedCommandMap.set(command, methodName); } getRecognizedCommands() { - return this._recognizedCommands.keys(); + return this._recognizedCommandMap.keys(); } /** @@ -69,7 +77,7 @@ class AbstractModule { } logger.debug("Attempting to call %s with %s", command, args); - let responseMessage = this.processMessage(command, args); + let responseMessage = this.processMessage(command, ...args); callback( room, @@ -81,16 +89,16 @@ class AbstractModule { Call the command method with the args */ processMessage(command, ...args) { - if (command in this._recognizedCommands) { - logger.debug("Calling %s with %s", this._recognizedCommands.get(command), args); - return this[this._recognizedCommands.get(command)](...args); + if (this._recognizedCommands.includes(command)) { + logger.debug("Calling %s with %s", this._recognizedCommandMap.get(command), args); + return this[this._recognizedCommandMap.get(command)](...args); } else { if (this.defaultCommand != null) { logger.debug("Attempting to use default command %s", this.defaultCommand); try { let newArgs = [command].concat(...args); - logger.debug("Calling %s with %s", this._recognizedCommands.get(this.defaultCommand), newArgs); - return this[this._recognizedCommands.get(this.defaultCommand)](...newArgs); + logger.debug("Calling %s with %s", this._recognizedCommandMap.get(this.defaultCommand), newArgs); + return this[this._recognizedCommandMap.get(this.defaultCommand)](...newArgs); } catch (e) { logger.error("Error while calling default command %s %s", this.defaultCommand, e); return this.cmd_help(); @@ -108,7 +116,7 @@ class AbstractModule { return basic help information,. */ cmd_help(...args) { - return message.createBasic(this.name + " HELP!"); + return message.createBasic(this.helpAndUsage); } } diff --git a/bot/module/admin.js b/bot/module/admin.js index 08dff6b..fafd4bd 100644 --- a/bot/module/admin.js +++ b/bot/module/admin.js @@ -11,6 +11,8 @@ class AdminModule extends AbstractModule { "Support administration tasks", "admin" ); + this.helpAndUsage = `Usage: admin + ...` } } diff --git a/bot/module/giphy.js b/bot/module/giphy.js index b0a9698..5fb6fb8 100644 --- a/bot/module/giphy.js +++ b/bot/module/giphy.js @@ -11,6 +11,8 @@ class GiphyModule extends AbstractModule { "Insert Giphy Links/Media", "giphy" ); + this.helpAndUsage = `Usage: !giphy itsworking + ...` } } From 827f9eb3ba46ca00f88b5d57b5d50b49cc2c8cec Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 01:47:28 -0600 Subject: [PATCH 16/26] Implemented a working Giphy module * Added example required config * Refactored config tools for more generic config loading * Added NODE_PATH to entrypoint.sh * NODE_PATH environment variable is now required to run Baphoment-JS * Added Axios library * Added config handling to AbstractModule and init process * Added basic working Giphy module --- bot/config.js | 25 ++++++++++-------- bot/engine.js | 21 ++++++++------- bot/module/abstract.js | 35 ++++++++++++++++++++++++- bot/module/admin.js | 2 +- bot/module/giphy.js | 29 ++++++++++++++++++++- bot/module/help.js | 6 ++--- data/giphy-config.json.example | 4 +++ entrypoint.sh | 3 +++ index.js | 2 +- package-lock.json | 47 ++++++++++++++++++++++++++++------ package.json | 1 + 11 files changed, 140 insertions(+), 35 deletions(-) create mode 100644 data/giphy-config.json.example diff --git a/bot/config.js b/bot/config.js index 9a00593..ad3e471 100644 --- a/bot/config.js +++ b/bot/config.js @@ -1,22 +1,25 @@ let { logger } = require('./logging'); -var configPath = null; -var config = null; +let loadedConfigs = new Map(); -function sanitizeConfig(config) { +function sanitizeConfig(config, fields=[]) { let clonedConfig = { ...config }; - clonedConfig.accessToken = "******" + fields.forEach((field) => { + clonedConfig[field] = '******' + }) return clonedConfig; } -function getConfig(configFile) { - if (config === null) { - configPath = configFile; - config = require(configFile); - logger.info("Loaded config:"); - logger.debug("%o", sanitizeConfig(config)); +function getConfig(configFile, sanitizedFields=[]) { + if (!loadedConfigs.has(configFile)) { + let config = require(configFile); + logger.info("Loaded config: %s", configFile); + logger.debug("%o", sanitizeConfig(config, sanitizedFields)); + loadedConfigs.set(configFile, config); + return config; + } else { + return loadedConfigs.get(configFile); } - return config; } exports.getConfig = getConfig \ No newline at end of file diff --git a/bot/engine.js b/bot/engine.js index 195bbf8..2d5a3c9 100644 --- a/bot/engine.js +++ b/bot/engine.js @@ -35,7 +35,7 @@ class Engine { let short3CharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 3); let shortCommandAliases = [shortCharCommand, short3CharCommand]; logger.info("Adding short command %s for module: %s", shortCommandAliases, mod.name); - shortCommandAliases.forEach( (commandAlias) => { + shortCommandAliases.forEach((commandAlias) => { this.commandMap.set(commandAlias, mod); }) }); @@ -116,14 +116,17 @@ class Engine { * @param {*} responseMessage */ function sendResponseMessage(bot, room, responseMessage) { - logger.debug("Responding to room: %s", room.roomId); - if (responseMessage instanceof Object) { - bot.client.sendMessage(room.roomId, responseMessage); - } else if (utility.isString(responseMessage)) { - bot.client.sendMessage(room.roomId, message.createBasic(responseMessage)); - } else { - logger.error("Unable to process response message: %s", 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)); + } else { + logger.error("Unable to process response message: %s", promisedMessage); + } + }) } /** diff --git a/bot/module/abstract.js b/bot/module/abstract.js index de16bac..38d2266 100644 --- a/bot/module/abstract.js +++ b/bot/module/abstract.js @@ -5,6 +5,7 @@ let { logger } = require('../logging'); let message = require('../message'); let { isFunction, getObjectKeysToPrototype } = require('../utility'); +let { getConfig } = require('../config'); class AbstractModule { /* @@ -32,16 +33,30 @@ class AbstractModule { The default method to call when a command word is not recognized. */ defaultCommand = null; + /* The module should be hidden from help and command dialogs. */ hidden = false; + /* This module should receive all messages, regardless of whether the module was directly referenced with a command. */ canHandleIndirectMessages = false; + /* + Indicates if the module requires a readable config file. + */ + needConfig = false; + + /* internal */ + + /* + The loaded config file, if it exists. + */ + _config = null; + constructor(name, description, command) { this.name = name; this.description = description; @@ -59,6 +74,14 @@ class AbstractModule { return this._recognizedCommandMap.keys(); } + getConfigFilePath() { + return process.env.NODE_PATH + '/data/' + this.name.toLowerCase().replace(' ', '_') + '-config.json'; + } + + getConfigSensitiveFields() { + return []; + } + /** * Default functionality for receiving and processing a message. * @@ -126,7 +149,17 @@ let abstractModulePrototype = Object.getPrototypeOf(new AbstractModule('', '', ' Initialization of a module. */ function init(mod) { - logger.debug("Initializing module %s", mod.name) + 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); + } + } + logger.debug("Detecting command methods."); let commandMethods = getObjectKeysToPrototype(mod, abstractModulePrototype, (key) => { return key.startsWith('cmd_') && isFunction(mod[key]); }) diff --git a/bot/module/admin.js b/bot/module/admin.js index fafd4bd..7b560e9 100644 --- a/bot/module/admin.js +++ b/bot/module/admin.js @@ -12,7 +12,7 @@ class AdminModule extends AbstractModule { "admin" ); this.helpAndUsage = `Usage: admin - ...` + ...`; } } diff --git a/bot/module/giphy.js b/bot/module/giphy.js index 5fb6fb8..2015b4e 100644 --- a/bot/module/giphy.js +++ b/bot/module/giphy.js @@ -3,6 +3,8 @@ */ let { AbstractModule } = require('./abstract'); +let axios = require('axios'); +let { logger } = require('../logging'); class GiphyModule extends AbstractModule { constructor() { @@ -12,7 +14,32 @@ class GiphyModule extends AbstractModule { "giphy" ); this.helpAndUsage = `Usage: !giphy itsworking - ...` + ...`; + this.needConfig = true; + this.defaultCommand = 'search'; + } + + getConfigSensitiveFields() { + return ["apiKey"]; + } + + 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, '******')); + return axios.get(url); + } + + /** + * Return the top item for the search terms. + * + * @param {...any} args + */ + cmd_search(...args) { + return this.getGiphySearch(args[0]) + .then((response) => { + // logger.debug("Giphy response: %o", response.data.data[0].url); + return response.data.data[0].embed_url; + }) } } diff --git a/bot/module/help.js b/bot/module/help.js index 5685b84..0ad96be 100644 --- a/bot/module/help.js +++ b/bot/module/help.js @@ -19,9 +19,9 @@ class HelpModule extends AbstractModule { _default_help_message() { let help = `!help `; - for (let command of this._commandList) { - help += "\n!help " + command + " : " + this._commandMap.get(command).description; - } + for (let command of this._commandList) { + help += "\n!help " + command + " : " + this._commandMap.get(command).description; + } return help; } diff --git a/data/giphy-config.json.example b/data/giphy-config.json.example new file mode 100644 index 0000000..ff1f1dd --- /dev/null +++ b/data/giphy-config.json.example @@ -0,0 +1,4 @@ +{ + "endpoint": "api.giphy.com/v1", + "apiKey": "" +} \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 02827e1..47ea9d7 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,5 +1,8 @@ #! /usr/bin/env sh +DIR="$( cd "$( dirname "${0}" )" >/dev/null 2>&1 && pwd )" +export NODE_PATH="${DIR}" + case $1 in run) node index.js diff --git a/index.js b/index.js index 56f2eaa..1e1f832 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,7 @@ let bot = require('./bot/bot'); let { getConfig } = require('./bot/config'); let engine = require('./bot/engine'); -let config = getConfig("../data/config.json") +let config = getConfig(process.env.NODE_PATH + "/data/config.json", ['accessToken']) engine.create( config, diff --git a/package-lock.json b/package-lock.json index 3765bb2..b11c55b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -86,6 +86,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -440,9 +449,9 @@ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-safe-stringify": { "version": "2.0.7", @@ -472,6 +481,29 @@ "is-buffer": "~2.0.3" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -607,8 +639,7 @@ "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" }, "is-callable": { "version": "1.1.4", @@ -962,9 +993,9 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "psl": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz", - "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" }, "punycode": { "version": "2.1.1", diff --git a/package.json b/package.json index 1c09411..528dff5 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "test": "mocha" }, "dependencies": { + "axios": "^0.19.0", "matrix-js-sdk": "2.4.5", "trie-prefix-tree": "^1.5.1", "winston": "^3.2.1" From be7f2d19ba294a59858d4d794b51510234b57a95 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 12:36:50 -0600 Subject: [PATCH 17/26] Updating docker configuration * Fleshed out the Dockerfile * Updated ignore files * Removed log files from repository * Added some logging to the entrypoint.sh * Updated fields in package.json * Changed the config module to load files without require * Added development run script --- .dockerignore | 9 ++++++-- .gitignore | 6 +++++- Dockerfile | 16 +++++++++++--- bot/config.js | 5 ++++- combined.log | 36 ------------------------------- entrypoint.sh | 6 +++++- error.log | 0 package.json | 2 ++ scripts/run_development_docker.sh | 21 ++++++++++++++++++ 9 files changed, 57 insertions(+), 44 deletions(-) delete mode 100644 combined.log delete mode 100644 error.log create mode 100644 scripts/run_development_docker.sh diff --git a/.dockerignore b/.dockerignore index ba561b3..0c31b8f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,7 @@ -data/*config.json -log/ \ No newline at end of file +node_modules/ +log/ +*.swp +pipeline.yml +README.md +CONTRIBUTING.md +test/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index bc3e975..08a93ea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ data/*config.json # Log files log/ +*.log # Mac Files -.DS_Store \ No newline at end of file +.DS_Store + +# Swap files +*.swp \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index e60840b..a1dbcdf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,16 @@ FROM node:12.14-stretch + COPY . /opt/baphomet -RUN npm install + +ENV NODE_ENV=production +ENV LOG_LEVEL=warn + +RUN mkdir /opt/baphomet/log + +RUN cd /opt/baphomet \ + && npm install --only=prod \ + && chmod +x entrypoint.sh + WORKDIR /opt/baphomet -ENTRYPOINT entrypoint.sh -CMD run \ No newline at end of file +ENTRYPOINT [ "./entrypoint.sh" ] +CMD [ "run" ] diff --git a/bot/config.js b/bot/config.js index ad3e471..9c1a7a7 100644 --- a/bot/config.js +++ b/bot/config.js @@ -1,3 +1,4 @@ +let fs = require('fs'); let { logger } = require('./logging'); let loadedConfigs = new Map(); @@ -12,7 +13,9 @@ function sanitizeConfig(config, fields=[]) { function getConfig(configFile, sanitizedFields=[]) { if (!loadedConfigs.has(configFile)) { - let config = require(configFile); + logger.info("Reading config: %s", configFile); + let rawConfigData = fs.readFileSync(configFile); + let config = JSON.parse(rawConfigData); logger.info("Loaded config: %s", configFile); logger.debug("%o", sanitizeConfig(config, sanitizedFields)); loadedConfigs.set(configFile, config); diff --git a/combined.log b/combined.log deleted file mode 100644 index e754a7d..0000000 --- a/combined.log +++ /dev/null @@ -1,36 +0,0 @@ -{"message":"Running with config:","level":"info","service":"user-service"} -{"message":{"baseUrl":"https://matrix.nulloctet.com","userId":"@baphomet-dev:nulloctet.com","accessToken":"******","admins":["@warrick:nulloctet.com"],"anyCanInvite":false,"statusRooms":["!ayFeLNSFDrerMMelCg:nulloctet.com"]},"level":"debug","service":"user-service"} -{"service":"user-service","level":"info","message":"Running version:"} -{"message":"Creating Matrix Client","level":"info","service":"user-service"} -{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} -{"service":"user-service","level":"debug","message":"Notifying %s"} -{"service":"user-service","level":"debug","message":"Notified %s"} -{"service":"user-service","level":"debug","message":"Notifying %s"} -{"service":"user-service","level":"debug","message":"Notified %s"} -{"message":"Running with config:","level":"info","service":"user-service"} -{"message":{"baseUrl":"https://matrix.nulloctet.com","userId":"@baphomet-dev:nulloctet.com","accessToken":"******","admins":["@warrick:nulloctet.com"],"anyCanInvite":false,"statusRooms":["!ayFeLNSFDrerMMelCg:nulloctet.com"]},"level":"debug","service":"user-service"} -{"service":"user-service","level":"info","message":"Running version:"} -{"message":"Creating Matrix Client","level":"info","service":"user-service"} -{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} -{"service":"user-service","level":"debug","message":"Notifying %s of startup"} -{"service":"user-service","level":"debug","message":"Notified %s of startup"} -{"service":"user-service","level":"debug","message":"Notifying %s of shutdown"} -{"service":"user-service","level":"debug","message":"Notified %s of shutdown"} -{"message":"Running with config:","level":"info","service":"user-service"} -{"message":{"baseUrl":"https://matrix.nulloctet.com","userId":"@baphomet-dev:nulloctet.com","accessToken":"******","admins":["@warrick:nulloctet.com"],"anyCanInvite":false,"statusRooms":["!ayFeLNSFDrerMMelCg:nulloctet.com"]},"level":"debug","service":"user-service"} -{"service":"user-service","level":"info","message":"Running version:"} -{"message":"Creating Matrix Client","level":"info","service":"user-service"} -{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} -{"service":"user-service","level":"debug","message":"Notifying %s of startup"} -{"service":"user-service","level":"debug","message":"Notified %s of startup"} -{"service":"user-service","level":"debug","message":"Notifying %s of shutdown"} -{"service":"user-service","level":"debug","message":"Notified %s of shutdown"} -{"message":"Running with config:","level":"info","service":"user-service"} -{"service":"user-service","level":"debug","message":"%o"} -{"service":"user-service","level":"info","message":"Running version: %s"} -{"message":"Creating Matrix Client","level":"info","service":"user-service"} -{"message":"Starting Matrix SDK Client","level":"info","service":"user-service"} -{"service":"user-service","level":"debug","message":"Notifying %s of startup"} -{"service":"user-service","level":"debug","message":"Notified %s of startup"} -{"service":"user-service","level":"debug","message":"Notifying %s of shutdown"} -{"service":"user-service","level":"debug","message":"Notified %s of shutdown"} diff --git a/entrypoint.sh b/entrypoint.sh index 47ea9d7..35d1320 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,6 +3,10 @@ DIR="$( cd "$( dirname "${0}" )" >/dev/null 2>&1 && pwd )" export NODE_PATH="${DIR}" +echo "NODE_ENV: ${NODE_ENV}" +echo "NODE_PATH: ${NODE_PATH}" +echo "LOG_LEVEL: ${LOG_LEVEL}" + case $1 in run) node index.js @@ -10,4 +14,4 @@ run) *) echo "\"$1\" is an unrecognized command" ;; -esac \ No newline at end of file +esac diff --git a/error.log b/error.log deleted file mode 100644 index e69de29..0000000 diff --git a/package.json b/package.json index 528dff5..b165106 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "main": "index.js", "author": "Drew Short ", "license": "MIT", + "description": "A Matrix bot written on top of the matrix-js-sdk", + "repository": "https://git.nulloctet.com/warricksothr/baphomet-js", "scripts": { "test": "mocha" }, diff --git a/scripts/run_development_docker.sh b/scripts/run_development_docker.sh new file mode 100644 index 0000000..3ca56c6 --- /dev/null +++ b/scripts/run_development_docker.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +pushd "${DIR}/.." + +CONTAINER_NAME=baphomet-dev +IMAGE_NAME=baphomet-js +IMAGE_TAG=dev +IMAGE_BUILD_DIR=. + +docker build -t ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_BUILD_DIR} + +docker rm ${CONTAINER_NAME} + +docker run -it \ + -e NODE_ENV=development \ + -e LOG_LEVEL=debug \ + --name ${CONTAINER_NAME} \ + ${IMAGE_NAME}:${IMAGE_TAG} + +popd \ No newline at end of file From 7cb47dbb2e08112988f498e717416071ab4f570c Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 12:43:36 -0600 Subject: [PATCH 18/26] Updating readme with development and build badges --- README.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 922c204..8a42433 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Baphomet +Development Branch Tests [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test-develop/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) +Master Branch Tests [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) + Baphomet is a bot to provide extended functionality to a matrix server ## Configuration @@ -10,12 +13,31 @@ Copy data/config.json.example to data/config.json and replace the relevent confi ```bash npm install -node index.js +./entrypoint.sh run ``` ## Development -TBD +### Requirements: + +Nodejs 12 LTS + +OR + +Docker + +#### Local: + +```bash +npm install +# +./entrypoint.sh run +``` + +#### Docker: +```bash +./scripts/run_development_docker.sh +``` ## Contributing From fcaed0fe1730b40b7c129d545cb7bfc885ca9c69 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 12:44:25 -0600 Subject: [PATCH 19/26] Fixing spacing in markdown --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a42433..c1e1dca 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Baphomet -Development Branch Tests [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test-develop/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) -Master Branch Tests [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) +Development Branch Tests: [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test-develop/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) + +Master Branch Tests: [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) Baphomet is a bot to provide extended functionality to a matrix server From 1a7eed5bff736c1455b8c8f124bd7d35bf003feb Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 12:47:28 -0600 Subject: [PATCH 20/26] Adding path ignores to build pipeline --- pipeline.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pipeline.yml b/pipeline.yml index fc0f644..eb10625 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -8,6 +8,13 @@ resources: private_key: | ((pull_key)) branch: master + ignore_paths: + - pipeline.yml + - scripts/upload_pipeline.sh + - README.md + - CONTRIBUTING.md + - LICENSE.md + - name: baphomet-js-git-develop type: git icon: git @@ -16,6 +23,12 @@ resources: private_key: | ((pull_key)) branch: develop + ignore_paths: + - pipeline.yml + - scripts/upload_pipeline.sh + - README.md + - CONTRIBUTING.md + - LICENSE.md jobs: - name: test From aa3e60a7addb8f466534e1268feb52a98aec8cd8 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 14:58:56 -0600 Subject: [PATCH 21/26] Working on updated pipeline with deployment --- README.md | 2 +- pipeline.yml | 75 ++++++++++++++++++++++++++++++++-------------- scripts/get_tag.sh | 2 ++ 3 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 scripts/get_tag.sh diff --git a/README.md b/README.md index c1e1dca..71879dd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Development Branch Tests: [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test-develop/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) -Master Branch Tests: [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) +Master Branch Tests: [![Concourse](https://concourse.nulloctet.com/api/v1/teams/nulloctet/pipelines/baphomet-js/jobs/test-release/badge)](https://concourse.nulloctet.com/teams/nulloctet/pipelines/baphomet-js) Baphomet is a bot to provide extended functionality to a matrix server diff --git a/pipeline.yml b/pipeline.yml index eb10625..7e1744a 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -1,28 +1,27 @@ --- resources: - - name: baphomet-js-git + - name: baphomet-js-git-develop type: git icon: git source: uri: ssh://git@git.nulloctet.com:8437/warricksothr/baphomet-js.git private_key: | ((pull_key)) - branch: master + branch: develop ignore_paths: - pipeline.yml - scripts/upload_pipeline.sh - README.md - CONTRIBUTING.md - LICENSE.md - - - name: baphomet-js-git-develop + - name: baphomet-js-git type: git icon: git source: uri: ssh://git@git.nulloctet.com:8437/warricksothr/baphomet-js.git private_key: | ((pull_key)) - branch: develop + branch: master ignore_paths: - pipeline.yml - scripts/upload_pipeline.sh @@ -31,31 +30,59 @@ resources: - LICENSE.md jobs: - - name: test +# Development Pipeline + - name: test-develop plan: - - get: baphomet-js-git + - get: baphomet-js-git-develop + trigger: true + - task: run-tests + config: + platform: linux + image_resource: + type: registry-image + source: { repository: node, tag: "12.14-stretch" } + inputs: + - name: baphomet-js-git-develop + run: + path: /bin/sh + args: + - -c + - | + echo "Node Version: $(node --version)" + echo "NPM Version: $(npm --version)" + cd baphomet-js-git-develop + npm install + npm test + - name: deploy-develop + plan: + - get: baphomet-js-git-develop + passed: [test-develop] trigger: true - - task: run-tests + - task: capture-version config: platform: linux image_resource: type: registry-image - source: { repository: node, tag: "12.14-stretch" } + source: { repository: bitnami/git, tag: "2-debian-9" } inputs: - - name: baphomet-js-git + - name: baphomet-js-git-develop + outputs: + - name: version run: path: /bin/sh args: - -c - | - echo "Node Version: $(node --version)" - echo "NPM Version: $(npm --version)" cd baphomet-js-git - npm install - npm test - - name: test-develop + chmod +x ././scripts/get_*.sh + echo $(./scripts/get_build.sh) > ../version/build.info + echo $(./scripts/get_version.sh) > ../version/version.info + echo $(./scripts/get_tag.sh) > ../version/tag.info + cat ../version/build.info +# Release Pipeline + - name: test-release plan: - - get: baphomet-js-git-develop + - get: baphomet-js-git trigger: true - task: run-tests config: @@ -64,7 +91,7 @@ jobs: type: registry-image source: { repository: node, tag: "12.14-stretch" } inputs: - - name: baphomet-js-git-develop + - name: baphomet-js-git run: path: /bin/sh args: @@ -72,13 +99,13 @@ jobs: - | echo "Node Version: $(node --version)" echo "NPM Version: $(npm --version)" - cd baphomet-js-git-develop + cd baphomet-js-git npm install npm test - - name: package + - name: package-release plan: - get: baphomet-js-git - passed: [test] + passed: [test-release] trigger: true - task: capture-version config: @@ -99,6 +126,7 @@ jobs: chmod +x ././scripts/get_*.sh echo $(./scripts/get_build.sh) > ../baphomet-js-version/build.info echo $(./scripts/get_version.sh) > ../baphomet-js-version/version.info + echo $(./scripts/get_tag.sh) > ../baphomet-js-version/tag.info cat ../baphomet-js-version/build.info - task: package config: @@ -118,13 +146,16 @@ jobs: - | mkdir tmp cd tmp - cp ../baphomet-js-version/version.info . cp ../baphomet-js-version/build.info . + cp ../baphomet-js-version/version.info . + cp ../baphomet-js-version/tag.info . cp ../baphomet-js-git/package*.json . cp ../baphomet-js-git/index.js . cp -r ../baphomet-js-git/assets . cp -r ../baphomet-js-git/bot . cp -r ../baphomet-js-git/data . + cp ../baphomet-js-git/entrypoint.js . + cp ../baphomet-js-git/Dockerfile . cp ../baphomet-js-git/README.md . cp ../baphomet-js-git/LICENSE.md . - tar -zcvf ../package/baphomet-js-$(cat version.info).tgz ./* + tar -zcvf ../package/baphomet-js-$(cat tag.info).tgz ./* diff --git a/scripts/get_tag.sh b/scripts/get_tag.sh new file mode 100644 index 0000000..9732476 --- /dev/null +++ b/scripts/get_tag.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +echo "$(git describe --tags | awk '{split($0,a,"-"); print a[1]}')" From 63d67034a06cf1fee6bdc57e8247f4b010085ebb Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 15:03:42 -0600 Subject: [PATCH 22/26] Cleanup pipeline part naming --- pipeline.yml | 67 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/pipeline.yml b/pipeline.yml index 7e1744a..6ce1317 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -1,6 +1,6 @@ --- resources: - - name: baphomet-js-git-develop + - name: git-develop type: git icon: git source: @@ -14,7 +14,7 @@ resources: - README.md - CONTRIBUTING.md - LICENSE.md - - name: baphomet-js-git + - name: git-master type: git icon: git source: @@ -28,12 +28,11 @@ resources: - README.md - CONTRIBUTING.md - LICENSE.md - jobs: # Development Pipeline - name: test-develop plan: - - get: baphomet-js-git-develop + - get: git-develop trigger: true - task: run-tests config: @@ -42,7 +41,7 @@ jobs: type: registry-image source: { repository: node, tag: "12.14-stretch" } inputs: - - name: baphomet-js-git-develop + - name: git-develop run: path: /bin/sh args: @@ -50,12 +49,12 @@ jobs: - | echo "Node Version: $(node --version)" echo "NPM Version: $(npm --version)" - cd baphomet-js-git-develop + cd git-develop npm install npm test - name: deploy-develop plan: - - get: baphomet-js-git-develop + - get: git-develop passed: [test-develop] trigger: true - task: capture-version @@ -65,7 +64,7 @@ jobs: type: registry-image source: { repository: bitnami/git, tag: "2-debian-9" } inputs: - - name: baphomet-js-git-develop + - name: git-develop outputs: - name: version run: @@ -73,7 +72,7 @@ jobs: args: - -c - | - cd baphomet-js-git + cd git-develop chmod +x ././scripts/get_*.sh echo $(./scripts/get_build.sh) > ../version/build.info echo $(./scripts/get_version.sh) > ../version/version.info @@ -82,7 +81,7 @@ jobs: # Release Pipeline - name: test-release plan: - - get: baphomet-js-git + - get: git-master trigger: true - task: run-tests config: @@ -91,7 +90,7 @@ jobs: type: registry-image source: { repository: node, tag: "12.14-stretch" } inputs: - - name: baphomet-js-git + - name: git-master run: path: /bin/sh args: @@ -99,12 +98,12 @@ jobs: - | echo "Node Version: $(node --version)" echo "NPM Version: $(npm --version)" - cd baphomet-js-git + cd git-master npm install npm test - name: package-release plan: - - get: baphomet-js-git + - get: git-master passed: [test-release] trigger: true - task: capture-version @@ -114,20 +113,20 @@ jobs: type: registry-image source: { repository: bitnami/git, tag: "2-debian-9" } inputs: - - name: baphomet-js-git + - name: git-master outputs: - - name: baphomet-js-version + - name: version run: path: /bin/sh args: - -c - | - cd baphomet-js-git + cd git-master chmod +x ././scripts/get_*.sh - echo $(./scripts/get_build.sh) > ../baphomet-js-version/build.info - echo $(./scripts/get_version.sh) > ../baphomet-js-version/version.info - echo $(./scripts/get_tag.sh) > ../baphomet-js-version/tag.info - cat ../baphomet-js-version/build.info + echo $(./scripts/get_build.sh) > ../version/build.info + echo $(./scripts/get_version.sh) > ../version/version.info + echo $(./scripts/get_tag.sh) > ../version/tag.info + cat ../version/build.info - task: package config: platform: linux @@ -135,8 +134,8 @@ jobs: type: registry-image source: { repository: debian, tag: "stretch-slim" } inputs: - - name: baphomet-js-git - - name: baphomet-js-version + - name: git-master + - name: version outputs: - name: package run: @@ -146,16 +145,16 @@ jobs: - | mkdir tmp cd tmp - cp ../baphomet-js-version/build.info . - cp ../baphomet-js-version/version.info . - cp ../baphomet-js-version/tag.info . - cp ../baphomet-js-git/package*.json . - cp ../baphomet-js-git/index.js . - cp -r ../baphomet-js-git/assets . - cp -r ../baphomet-js-git/bot . - cp -r ../baphomet-js-git/data . - cp ../baphomet-js-git/entrypoint.js . - cp ../baphomet-js-git/Dockerfile . - cp ../baphomet-js-git/README.md . - cp ../baphomet-js-git/LICENSE.md . + cp ../version/build.info . + cp ../version/version.info . + cp ../version/tag.info . + cp ../git-master/package*.json . + cp ../git-master/index.js . + cp -r ../git-master/assets . + cp -r ../git-master/bot . + cp -r ../git-master/data . + cp ../git-master/entrypoint.js . + cp ../git-master/Dockerfile . + cp ../git-master/README.md . + cp ../git-master/LICENSE.md . tar -zcvf ../package/baphomet-js-$(cat tag.info).tgz ./* From 7becc771753b3b4b689d76a92970cdd890785b11 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 15:19:16 -0600 Subject: [PATCH 23/26] Added develop package and deploy step to pipeline --- pipeline.yml | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/pipeline.yml b/pipeline.yml index 6ce1317..7950b3d 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -28,6 +28,14 @@ resources: - README.md - CONTRIBUTING.md - LICENSE.md + - name: docker-image + type: docker-image + icon: docker + source: + repository: ((nexus_docker_write.host))/nulloctet/baphomet-js + username: ((nexus_docker_write.username)) + password: ((nexus_docker_write.password)) + jobs: # Development Pipeline - name: test-develop @@ -78,6 +86,41 @@ jobs: echo $(./scripts/get_version.sh) > ../version/version.info echo $(./scripts/get_tag.sh) > ../version/tag.info cat ../version/build.info + - task: package + config: + platform: linux + image_resource: + type: registry-image + source: { repository: debian, tag: "stretch-slim" } + inputs: + - name: git-develop + - name: version + outputs: + - name: package + run: + path: /bin/sh + args: + - -c + - | + mkdir tmp + cd tmp + cp ../version/build.info . + cp ../version/version.info . + cp ../version/tag.info . + cp ../git-master/package*.json . + cp ../git-master/index.js . + cp -r ../git-master/assets . + cp -r ../git-master/bot . + cp -r ../git-master/data . + cp ../git-master/entrypoint.js . + cp ../git-master/Dockerfile . + cp ../git-master/README.md . + cp ../git-master/LICENSE.md . + - put: docker-image + params: + build: package + tag_file: package/tag.info + tag_as_latest: false # Release Pipeline - name: test-release plan: From b12fa7eba1dfbe03200b8057ba68f151f1884554 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 15:31:56 -0600 Subject: [PATCH 24/26] Completed development image deployment pipeline --- pipeline.yml | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/pipeline.yml b/pipeline.yml index 7950b3d..4644a6c 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -60,7 +60,7 @@ jobs: cd git-develop npm install npm test - - name: deploy-develop + - name: deploy-develop-image plan: - get: git-develop passed: [test-develop] @@ -85,6 +85,7 @@ jobs: echo $(./scripts/get_build.sh) > ../version/build.info echo $(./scripts/get_version.sh) > ../version/version.info echo $(./scripts/get_tag.sh) > ../version/tag.info + echo "dev" > ../version/tag cat ../version/build.info - task: package config: @@ -102,24 +103,22 @@ jobs: args: - -c - | - mkdir tmp - cd tmp - cp ../version/build.info . - cp ../version/version.info . - cp ../version/tag.info . - cp ../git-master/package*.json . - cp ../git-master/index.js . - cp -r ../git-master/assets . - cp -r ../git-master/bot . - cp -r ../git-master/data . - cp ../git-master/entrypoint.js . - cp ../git-master/Dockerfile . - cp ../git-master/README.md . - cp ../git-master/LICENSE.md . + cd package + cp ../version/* . + cp ../git-develop/package*.json . + cp ../git-develop/index.js . + cp -r ../git-develop/assets . + cp -r ../git-develop/bot . + cp -r ../git-develop/data . + cp ../git-develop/entrypoint.sh . + cp ../git-develop/Dockerfile . + cp ../git-develop/README.md . + cp ../git-develop/LICENSE.md . + ls -al . - put: docker-image params: build: package - tag_file: package/tag.info + tag_file: package/tag tag_as_latest: false # Release Pipeline - name: test-release @@ -169,6 +168,7 @@ jobs: echo $(./scripts/get_build.sh) > ../version/build.info echo $(./scripts/get_version.sh) > ../version/version.info echo $(./scripts/get_tag.sh) > ../version/tag.info + cp ../version/tag.info ../version/tag cat ../version/build.info - task: package config: @@ -188,15 +188,13 @@ jobs: - | mkdir tmp cd tmp - cp ../version/build.info . - cp ../version/version.info . - cp ../version/tag.info . + cp ../version/* . cp ../git-master/package*.json . cp ../git-master/index.js . cp -r ../git-master/assets . cp -r ../git-master/bot . cp -r ../git-master/data . - cp ../git-master/entrypoint.js . + cp ../git-master/entrypoint.sh . cp ../git-master/Dockerfile . cp ../git-master/README.md . cp ../git-master/LICENSE.md . From 6fb88057b54e4758ea270ae215abc6f067dd5926 Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 15:35:04 -0600 Subject: [PATCH 25/26] Added some useful output to pipeline steps --- pipeline.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pipeline.yml b/pipeline.yml index 4644a6c..b228bbb 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -86,7 +86,10 @@ jobs: echo $(./scripts/get_version.sh) > ../version/version.info echo $(./scripts/get_tag.sh) > ../version/tag.info echo "dev" > ../version/tag - cat ../version/build.info + echo "Build Information: $(cat ../version/build.info)" + echo "Version Information: $(cat ../version/version.info)" + echo "Tag Information: $(cat ../version/tag.info)" + echo "Docker Image Tag: $(cat ../version/tag)" - task: package config: platform: linux @@ -169,7 +172,10 @@ jobs: echo $(./scripts/get_version.sh) > ../version/version.info echo $(./scripts/get_tag.sh) > ../version/tag.info cp ../version/tag.info ../version/tag - cat ../version/build.info + echo "Build Information: $(cat ../version/build.info)" + echo "Version Information: $(cat ../version/version.info)" + echo "Tag Information: $(cat ../version/tag.info)" + echo "Docker Image Tag: $(cat ../version/tag)" - task: package config: platform: linux @@ -186,8 +192,7 @@ jobs: args: - -c - | - mkdir tmp - cd tmp + cd package cp ../version/* . cp ../git-master/package*.json . cp ../git-master/index.js . @@ -198,4 +203,4 @@ jobs: cp ../git-master/Dockerfile . cp ../git-master/README.md . cp ../git-master/LICENSE.md . - tar -zcvf ../package/baphomet-js-$(cat tag.info).tgz ./* + ls -al . From 01f90c8aaec003a23e6ffd5aa6d995227ddaf05b Mon Sep 17 00:00:00 2001 From: Drew Short Date: Mon, 30 Dec 2019 15:38:46 -0600 Subject: [PATCH 26/26] Adding pipeline pieces deploy release images --- pipeline.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pipeline.yml b/pipeline.yml index b228bbb..9bd3f0b 100644 --- a/pipeline.yml +++ b/pipeline.yml @@ -146,7 +146,7 @@ jobs: cd git-master npm install npm test - - name: package-release + - name: deploy-release-image plan: - get: git-master passed: [test-release] @@ -204,3 +204,8 @@ jobs: cp ../git-master/README.md . cp ../git-master/LICENSE.md . ls -al . + - put: docker-image + params: + build: package + tag_file: package/tag + tag_as_latest: true