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. 15
      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');
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);
}
}
exports.getConfig = getConfig

15
bot/engine.js

@ -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));
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", responseMessage);
logger.error("Unable to process response message: %s", promisedMessage);
}
})
}
/**

35
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]);
})

2
bot/module/admin.js

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

29
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;
})
}
}

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
DIR="$( cd "$( dirname "${0}" )" >/dev/null 2>&1 && pwd )"
export NODE_PATH="${DIR}"
case $1 in
run)
node index.js

2
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,

47
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",

1
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"

Loading…
Cancel
Save