Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update syntax to ES6 #45

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
26 changes: 26 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"env": {
"jasmine": true
},
"extends": "airbnb-base",
"parserOptions": {
"sourceType": "script"
},
"rules": {
"brace-style": [2, "stroustrup"],
"consistent-return": 0,
"curly": [2, "all"],
"function-paren-newline": 0,
"import/no-dynamic-require": 0,
"import/no-extraneous-dependencies": [2, {"devDependencies": ["**/*spec.js"]}],
"indent": [2, 4],
"max-len": [2, 100],
"newline-per-chained-call": 0,
"no-multi-assign": 0,
"no-multi-spaces": [2, {"ignoreEOLComments": true}],
"no-shadow": [2, {"allow": ["err"]}],
"no-underscore-dangle": [2, {"allowAfterThis": true}],
"object-curly-spacing": [2, "never"],
"radix": [2, "as-needed"]
}
}
6 changes: 4 additions & 2 deletions borgil.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
var Bot = require('./bot/bot');
'use strict';

const Bot = require('./bot/bot');

var borgil = new Bot();

module.exports = new Bot();
157 changes: 133 additions & 24 deletions bot/bot.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,146 @@
var EventEmitter = require('eventemitter2').EventEmitter2;
var util = require('util');
'use strict';

var Config = require('./config');
var Plugin = require('./plugin');
const EventEmitter = require('eventemitter2').EventEmitter2;
const fs = require('fs');
const handlebars = require('handlebars');
const moment = require('moment-timezone');
const path = require('path');
const winston = require('winston');

const Config = require('./config');
const Plugin = require('./plugin');

// The bot object.
var Bot = module.exports = function (configfile) {
// Run the event emitter constructor.
EventEmitter.call(this);

this.config = new Config(configfile);
const logDefaults = {
dir: 'logs',
filename_template: 'bot--{{date}}.log',
date_format: 'YYYY-MM-DD--HH-mm-ss',
console: false,
debug: false,
};
const defaultBuffer = 100;

this.plugins = {};
this.memory = {};
module.exports = class Bot extends EventEmitter {
constructor(configfile) {
super();

// Include extra functionality.
require('./log')(this);
require('./transports')(this);
require('./buffers')(this);
this.config = new Config(configfile);

// Activate all plugins mentioned in the plugins section of the config.
for (pluginName in this.config.get('plugins', {})) {
this.log.info('Activating plugin:', pluginName);
// A shared key/value store that all plugins can read and write to.
this.memory = new Map();

this.initLog();
this.initBuffers();
this.initTransports();
this.initPlugins();
}

initLog() {
const level = this.config.get('log.debug') ? 'debug' : 'info';
const renderFilename = handlebars.compile(
this.config.get('log.filename_template', logDefaults.filename_template));

const logdir = this.config.get('log.dir', logDefaults.dir);
try {
var plugin = new Plugin(this, pluginName);
fs.mkdirSync(logdir);
}
catch (err) {
this.log.warn('Error activating plugin %s:', pluginName, err.message);
continue;
if (err.code !== 'EEXIST') {
throw err;
}
}
this.plugins[pluginName] = plugin;

const dateFormat = this.config.get('log.date_format', logDefaults.dateFormat);
const timezone = this.config.get('log.timezone');
const logfile = path.join(logdir, renderFilename({
date: (timezone ? moment.tz(timezone) : moment()).format(dateFormat),
}));

const transports = [];
if (logfile) {
transports.push(new winston.transports.File({
filename: logfile,
json: false,
level,
timestamp: true,
}));
}
if (this.config.get('log.console')) {
transports.push(new winston.transports.Console({
colorize: true,
level,
timestamp: true,
}));
}

this.log = new winston.Logger({
transports,
});
}

initBuffers() {
// Create buffer objects for each client.
this.buffers = {};

// Log each message to a buffer.
this.on('message', (transport, msg) => {
// Initialize buffer for this transport and source if necessary.
if (!(transport.name in this.buffers)) {
this.buffers[transport.name] = {};
}
if (!(msg.replyto in this.buffers[transport.name])) {
this.buffers[transport.name][msg.replyto] = [];
}
const buffer = this.buffers[transport.name][msg.replyto];

// Trim buffer to maximum length, then add this message.
if (buffer.length >= this.config.get('buffer', defaultBuffer)) {
buffer.pop();
}
buffer.unshift(msg);
});
}

// Activate all transports mentioned in the transports section of the config.
initTransports() {
const tpConfigs = this.config.get('transports', {});

this.transports = {};
Object.keys(tpConfigs).forEach((tpName) => {
this.log.info('Activating transport:', tpName);
const tpConfig = tpConfigs[tpName];
let transport;
try {
// eslint-disable-next-line global-require
const TpType = require(`./transports/${tpConfig.type}`);
transport = new TpType(this, tpName, tpConfig);
}
catch (err) {
this.log.warn('Error activating transport %s:', tpName, err.message);
return;
}
this.transports[tpName] = transport;
});

this.log.info(Object.keys(this.transports).length, 'transports(s) activated.');
}

// Activate all plugins mentioned in the plugins section of the config.
initPlugins() {
this.plugins = {};
Object.keys(this.config.get('plugins', {})).forEach((pluginName) => {
this.log.info('Activating plugin:', pluginName);
let plugin;
try {
plugin = new Plugin(this, pluginName);
}
catch (err) {
this.log.warn('Error activating plugin %s:', pluginName, err.message);
return;
}
this.plugins[pluginName] = plugin;
});

this.log.info(Object.keys(this.plugins).length, 'plugin(s) activated.');
}
};
// Extend the event emitter class.
util.inherits(Bot, EventEmitter);
24 changes: 0 additions & 24 deletions bot/buffers.js

This file was deleted.

125 changes: 66 additions & 59 deletions bot/config.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,94 @@
var extend = require('extend');
var fs = require('fs');
var yaml = require('js-yaml');
'use strict';

const extend = require('extend');
const fs = require('fs');
const yaml = require('js-yaml');


function getValue(obj, elems) {
if (elems.length == 1) {
const value = obj[elems[0]];

if (elems.length === 1) {
// Leaf node.
return obj[elems[0]];
return value;
}

if (typeof obj[elems[0]] != 'object') {
if (!value || typeof value !== 'object') {
// Branch doesn't exist.
return undefined;
}

// Move down the tree.
return getValue(obj[elems[0]], elems.slice(1));
return getValue(value, elems.slice(1));
}

function splitPath(key, value) {
value = splitValue(value);
module.exports = class Config {
constructor(configInit) {
this.config = {};

// If the key is a dotted path, split off the first element and parse the rest.
var tree = {};
var kelem = Array.isArray(key) ? key : key.split('.');
tree[kelem[0]] = kelem.length == 1 ? value : splitPath(kelem.slice(1), value);
return tree;
}
if (typeof configInit === 'object') {
// If an object is passed, use it as the config.
this.config = Config.splitValue(configInit);
}
else {
// Otherwise treat it as a filename and read that file as YAML or JSON.
// Fall back to default filenames if no existing filename was passed.
const configPath = [configInit, 'config.json', 'config.yml']
.find(fs.existsSync.bind(fs));
if (!configPath) {
return console.error('Config file not found.');
}

function splitValue(value) {
if (Array.isArray(value)) {
// Parse each value individually.
return value.map(splitValue);
}
if (typeof value === 'object') {
// Parse each key and value individually.
var tree = {};
for (key in value) {
extend(true, tree, splitPath(key, value[key]));
const configFile = fs.readFileSync(configPath, 'utf-8');

try {
if (configPath.match(/\.ya?ml$/i)) {
this.config = Config.splitValue(yaml.safeLoad(configFile));
}
else {
this.config = Config.splitValue(JSON.parse(configFile));
}
}
catch (err) {
console.error(`Error reading config file at ${configPath}:`, err.message);
}
}
return tree;
}
return value;
}

var Config = module.exports = function (config_init) {
this.config = {};
static splitPath(key, value) {
const val = Config.splitValue(value);

if (typeof config_init == 'object') {
// If an object is passed, use it as the config.
this.config = splitValue(config_init);
// If the key is a dotted path, split off the first element and parse the rest.
const tree = {};
const kelem = Array.isArray(key) ? key : key.split('.');
tree[kelem[0]] = kelem.length === 1 ? val : Config.splitPath(kelem.slice(1), val);
return tree;
}
else {
// Otherwise treat it as a filename and read that file as YAML or JSON.
// Fall back to default filenames if no existing filename was passed.
var configpath = [config_init, 'config.json', 'config.yml'].find(fs.existsSync.bind(fs));
if (!configpath) {
return console.error('Config file not found.');
}

var configfile = fs.readFileSync(configpath, 'utf-8');
static splitValue(value) {
if (Array.isArray(value)) {
// Parse each value individually.
return value.map(Config.splitValue);
}

try {
if (configpath.match(/\.ya?ml$/i)) this.config = splitValue(yaml.safeLoad(configfile));
else this.config = splitValue(JSON.parse(configfile));
} catch (e) {
console.error('Error reading config file at ' + configpath + ':', e.message);
if (value && typeof value === 'object') {
// Parse each key and value individually.
const tree = {};
Object.keys(value).forEach((key) => {
extend(true, tree, Config.splitPath(key, value[key]));
});
return tree;
}
}
};

Config.prototype.get = function (path, defval) {
var value = getValue(this.config, Array.isArray(path) ? path : path.split('.'));
return value !== undefined ? value : defval;
};
return value;
}

Config.prototype.set = function (path, value) {
//setValue(this.config, path, value);
extend(true, this.config, splitPath(path, value));
};
get(path, defval) {
const value = getValue(this.config, Array.isArray(path) ? path : path.split('.'));
return (value === undefined || value === null) ? defval : value;
}

Config.prototype.save = function () {
if (typeof config_init == 'string') {
fs.writeFile(config_init, JSON.stringify(this.config, null, 4), {encoding: 'utf-8'});
set(path, value) {
extend(true, this.config, Config.splitPath(path, value));
}
};
Loading