Browse Source

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
pull/10/head
Drew Short 5 years ago
parent
commit
827f9eb3ba
  1. 25
      bot/config.js
  2. 17
      bot/engine.js
  3. 35
      bot/module/abstract.js
  4. 2
      bot/module/admin.js
  5. 29
      bot/module/giphy.js
  6. 4
      data/giphy-config.json.example
  7. 3
      entrypoint.sh
  8. 2
      index.js
  9. 47
      package-lock.json
  10. 1
      package.json

25
bot/config.js

@ -1,22 +1,25 @@
let { logger } = require('./logging'); let { logger } = require('./logging');
var configPath = null;
var config = null;
let loadedConfigs = new Map();
function sanitizeConfig(config) {
function sanitizeConfig(config, fields=[]) {
let clonedConfig = { ...config }; let clonedConfig = { ...config };
clonedConfig.accessToken = "******"
fields.forEach((field) => {
clonedConfig[field] = '******'
})
return clonedConfig; 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; return config;
} else {
return loadedConfigs.get(configFile);
}
} }
exports.getConfig = getConfig exports.getConfig = getConfig

17
bot/engine.js

@ -35,7 +35,7 @@ class Engine {
let short3CharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 3); let short3CharCommand = getShortestPrefix(this.commandRadixTree, mod.command, 3);
let shortCommandAliases = [shortCharCommand, short3CharCommand]; let shortCommandAliases = [shortCharCommand, short3CharCommand];
logger.info("Adding short command %s for module: %s", shortCommandAliases, mod.name); logger.info("Adding short command %s for module: %s", shortCommandAliases, mod.name);
shortCommandAliases.forEach( (commandAlias) => {
shortCommandAliases.forEach((commandAlias) => {
this.commandMap.set(commandAlias, mod); this.commandMap.set(commandAlias, mod);
}) })
}); });
@ -116,14 +116,17 @@ class Engine {
* @param {*} responseMessage * @param {*} responseMessage
*/ */
function sendResponseMessage(bot, room, 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));
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 { } else {
logger.error("Unable to process response message: %s", responseMessage);
logger.error("Unable to process response message: %s", promisedMessage);
} }
})
} }
/** /**

35
bot/module/abstract.js

@ -5,6 +5,7 @@
let { logger } = require('../logging'); let { logger } = require('../logging');
let message = require('../message'); let message = require('../message');
let { isFunction, getObjectKeysToPrototype } = require('../utility'); let { isFunction, getObjectKeysToPrototype } = require('../utility');
let { getConfig } = require('../config');
class AbstractModule { class AbstractModule {
/* /*
@ -32,16 +33,30 @@ class AbstractModule {
The default method to call when a command word is not recognized. The default method to call when a command word is not recognized.
*/ */
defaultCommand = null; defaultCommand = null;
/* /*
The module should be hidden from help and command dialogs. The module should be hidden from help and command dialogs.
*/ */
hidden = false; hidden = false;
/* /*
This module should receive all messages, regardless of whether This module should receive all messages, regardless of whether
the module was directly referenced with a command. the module was directly referenced with a command.
*/ */
canHandleIndirectMessages = false; 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) { constructor(name, description, command) {
this.name = name; this.name = name;
this.description = description; this.description = description;
@ -59,6 +74,14 @@ class AbstractModule {
return this._recognizedCommandMap.keys(); 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. * Default functionality for receiving and processing a message.
* *
@ -126,7 +149,17 @@ let abstractModulePrototype = Object.getPrototypeOf(new AbstractModule('', '', '
Initialization of a module. Initialization of a module.
*/ */
function init(mod) { 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) => { let commandMethods = getObjectKeysToPrototype(mod, abstractModulePrototype, (key) => {
return key.startsWith('cmd_') && isFunction(mod[key]); return key.startsWith('cmd_') && isFunction(mod[key]);
}) })

2
bot/module/admin.js

@ -12,7 +12,7 @@ class AdminModule extends AbstractModule {
"admin" "admin"
); );
this.helpAndUsage = `Usage: admin <command> this.helpAndUsage = `Usage: admin <command>
...`
...`;
} }
} }

29
bot/module/giphy.js

@ -3,6 +3,8 @@
*/ */
let { AbstractModule } = require('./abstract'); let { AbstractModule } = require('./abstract');
let axios = require('axios');
let { logger } = require('../logging');
class GiphyModule extends AbstractModule { class GiphyModule extends AbstractModule {
constructor() { constructor() {
@ -12,7 +14,32 @@ class GiphyModule extends AbstractModule {
"giphy" "giphy"
); );
this.helpAndUsage = `Usage: !giphy itsworking 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;
})
} }
} }

4
data/giphy-config.json.example

@ -0,0 +1,4 @@
{
"endpoint": "api.giphy.com/v1",
"apiKey": "<Your API Key Here>"
}

3
entrypoint.sh

@ -1,5 +1,8 @@
#! /usr/bin/env sh #! /usr/bin/env sh
DIR="$( cd "$( dirname "${0}" )" >/dev/null 2>&1 && pwd )"
export NODE_PATH="${DIR}"
case $1 in case $1 in
run) run)
node index.js node index.js

2
index.js

@ -2,7 +2,7 @@ let bot = require('./bot/bot');
let { getConfig } = require('./bot/config'); let { getConfig } = require('./bot/config');
let engine = require('./bot/engine'); let engine = require('./bot/engine');
let config = getConfig("../data/config.json")
let config = getConfig(process.env.NODE_PATH + "/data/config.json", ['accessToken'])
engine.create( engine.create(
config, config,

47
package-lock.json

@ -86,6 +86,15 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz",
"integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==" "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": { "babel-runtime": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
@ -440,9 +449,9 @@
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
}, },
"fast-json-stable-stringify": { "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": { "fast-safe-stringify": {
"version": "2.0.7", "version": "2.0.7",
@ -472,6 +481,29 @@
"is-buffer": "~2.0.3" "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": { "forever-agent": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@ -607,8 +639,7 @@
"is-buffer": { "is-buffer": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", "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": { "is-callable": {
"version": "1.1.4", "version": "1.1.4",
@ -962,9 +993,9 @@
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"psl": { "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": { "punycode": {
"version": "2.1.1", "version": "2.1.1",

1
package.json

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

Loading…
Cancel
Save