<commit_before>/*jshint node:true */

'use strict';

var map   = require('map-stream'),
	gutil   = require('gulp-util'),
	os      = require('os'),
	exec    = require('child_process').exec;

module.exports = function(command, opt) {
	// Assign default options if one is not supplied
	opt = opt || {};
	opt = {

		// general settings
		silent:             opt.silent              || false,
		debug:              opt.debug               || false,
		clear:              opt.clear               || false,
		dryRun:             opt.dryRun               || false,

		// code coverage options
		coverageClover:     opt.coverageClover      || '',
		coverageCrap4j:     opt.coverageCrap4j      || '',
		coverageHtml:       opt.coverageHtml        || '',
		coveragePhp:        opt.coveragePhp         || '',
		coverageText:       opt.coverageText        || '',
		coverageXml:        opt.coverageXml         || '',

		// logging options
		logJunit:           opt.logJunit            || '',
		logTap:             opt.logTap              || '',
		logJson:            opt.logJson             || '',
		testdoxHtml:        opt.testdoxHtml         || '',
		testdoxText:        opt.testdoxText         || '',

		// test selection options
		filter:             opt.filter              || '',
		testClass:          opt.testClass           || '',
		testSuite:          opt.testSuite           || '',
		group:              opt.group               || '',
		excludeGroup:       opt.excludeGroup        || '',
		listGroups:         opt.listGroups          || '',
		testSuffix:         opt.testSuffix          || '',

		// test execution options
		reportUselessTests: opt.reportUselessTests || false,
		strictCoverage:     opt.strictCoverage     || false,
		disallowTestOutput: opt.disallowTestOutput || false,
		enforceTimeLimit:   opt.enforceTimeLimit   || false,
		disallowTodoTests:  opt.disallowTestOutput || false,
		strict:             opt.strict             || false,

		processIsolation:   opt.processIsolation   || false,
		noGlobalsBackup:    opt.noGlobalsBackup    || false,
		staticBackup:       opt.staticBackup       || false,

		colors:             opt.colors             || true,
		stderr:             opt.stderr             || false,
		stopOnError:        opt.stopOnError        || false,
		stopOnFailure:      opt.stopOnFailure      || false,
		stopOnRisky:        opt.stopOnRisky        || false,
		stopOnSkipped:      opt.stopOnSkipped      || false,
		stopOnIncomplete:   opt.stopOnIncomplete   || false,
		verbose:            opt.verbose            || false,

		loader:             opt.loader             || '',
		repeat:             opt.repeat             || '',
		tap:                opt.tap                || false,
		testdox:            opt.testdox            || false,
		printer:            opt.printer            || '',

		// configuration options
		bootstrap:          opt.bootstrap          || '',
		configurationFile:  opt.configurationFile  || false,
		noConfiguration:    opt.noConfiguration    || false,
		includePath:        opt.includePath        || ''

	};

	// If path to phpunit bin not supplied, use default vendor/bin path
	if (!command) {
		command = './vendor/bin/phpunit';

		// Use the backslashes on Windows
		if (os.platform() === 'win32') {
			command = command.replace(/[/]/g, '\\');
		}
	} else if (typeof command !== 'string') {
		throw new gutil.PluginError('gulp-phpunit', 'Invalid PHPUnit Binary');
	}

	var launched = false;

	return map( function(file, cb) {
		// First file triggers the command, so other files does not matter
		if (launched) {
			return cb(null, file);
		}
		launched = true;

		var cmd = opt.clear ? 'clear && ' + command : command;

		// add remaining switches

		/* code coverage */
		if(opt.coverageClover)      { cmd += ' --coverage-clover=' + opt.coverageClover; }
		if(opt.coverageCrap4j)      { cmd += ' --coverage-crap4j=' + opt.coverageCrap4j; }
		if(opt.coverageHtml)        { cmd += ' --coverage-html=' + opt.coverageHtml; }
		if(opt.coveragePhp)         { cmd += ' --coverage-php=' + opt.coveragePhp; }
		if(opt.coverageText)        { cmd += ' --coverage-text=' + opt.coverageText; }
		if(opt.coverageXml)         { cmd += ' --coverage-xml=' + opt.coverageXml; }

		/* logging options */
		if(opt.logJunit)            { cmd += ' --log-junit=' + opt.logJunit; }
		if(opt.logTap)              { cmd += ' --log-tap=' + opt.logTap; }
		if(opt.logJson)             { cmd += ' --log-json=' + opt.logJson; }
		if(opt.testdoxHtml)         { cmd += ' --testdox-html=' + opt.testdoxHtml; }
		if(opt.testdoxText)         { cmd += ' --testdox-text=' + opt.testdoxText; }

		/* test selection */
		if(opt.filter)              { cmd += ' --filter=' + opt.filter; }
		if(opt.group)               { cmd += ' --group=' + opt.group; }
		if(opt.excludeGroup)        { cmd += ' --exclude-group=' + opt.excludeGroup; }
		if(opt.listGroups)          { cmd += ' --list-groups=' + opt.listGroups; }
		if(opt.testSuffix)          { cmd += ' --test-suffix=' + opt.testSuffix; }

		/* test execution options */
		if(opt.reportUselessTests)  { cmd += ' --report-useless-tests'; }
		if(opt.strictCoverage)      { cmd += ' --strict-coverage'; }
		if(opt.disallowTestOutput)  { cmd += ' --disallow-test-output'; }
		if(opt.enforceTimeLimit)    { cmd += ' --enforce-time-limit'; }
		if(opt.disallowTodoTests)   { cmd += ' --disallow-todo-tests'; }
		if(opt.strict)              { cmd += ' --strict'; }
		if(opt.processIsolation)    { cmd += ' --process-isolation'; }
		if(opt.noGlobalsBackup)     { cmd += ' --no-globals-backup'; }
		if(opt.staticBackup)        { cmd += ' --static-backup'; }
		if(opt.colors)              { cmd += ' --colors'; }
		if(opt.stderr)              { cmd += ' --stderr'; }
		if(opt.stopOnError)         { cmd += ' --stop-on-error'; }
		if(opt.stopOnFailure)       { cmd += ' --stop-on-failure'; }
		if(opt.stopOnRisky)         { cmd += ' --stop-on-risky'; }
		if(opt.stopOnSkipped)       { cmd += ' --stop-on-skipped'; }
		if(opt.stopOnIncomplete)    { cmd += ' --stop-on-incomplete'; }
		if(opt.loader)              { cmd += ' --loader=' + opt.loader; }
		if(opt.repeat)              { cmd += ' --repeat=' + opt.repeat; }
		if(opt.tap)                 { cmd += ' --tap'; }
		if(opt.testdox)             { cmd += ' --testdox'; }
		if(opt.printer)             { cmd += ' --printer=' + opt.printer; }
		if (opt.debug)              { cmd += ' --debug'; }

		/* configuration options */
		if(opt.bootstrap)           { cmd += ' --bootstrap=' + opt.bootstrap; }
		if (opt.noConfiguration)    { cmd += ' --no-configuration'; }
		if (opt.includePath)        { cmd += ' --include-path=' + opt.includePath; }

		/* ---- FINALLY add testClass, Suite or Configuration -- */

		// after options and switches are added, then add either testClass or testSuite

		// Priority:
		// - configuration file
		// - testSuite
		// - testClass

		var skip;

		if ((opt.configurationFile) && (! skip) && (!opt.noConfiguration)){
			cmd += ' -c ' + opt.configurationFile;
			skip = true;
		}

		if ((opt.testSuite) && (! skip)) {
			cmd += ' ' + opt.testSuite;
			skip = true;
		}

		if ((opt.testClass) && (! skip)) {
			cmd += ' ' + opt.testClass;
			skip = true;
		}

		// append debug code if switch enabled
		if ((opt.debug) || (opt.dryRun)){
			gutil.log(gutil.colors.yellow('\n       *** Debug Cmd: ' + cmd  + ' ***\n'));
		}

		/* -- EXECUTE -- */
		if( ! opt.dryRun ) {
			exec(cmd, function (error, stdout, stderr) {
				if (!opt.silent && stderr) {
					gutil.log(stderr);
				}

				if (!opt.silent) {
					// Trim trailing cr-lf
					stdout = stdout.trim();

					if (stdout) {
						gutil.log(stdout);
					}
				}

				// call user callback if ano error occurs
				if (error) {
					if (opt.debug) {
						gutil.log(error);
					}
					cb(error, file);
				} else {
					cb(null, file);
				}

			});
		}

	});
};
<commit_msg>updated dry run output text color<commit_after>/*jshint node:true */

'use strict';

var map   = require('map-stream'),
	gutil   = require('gulp-util'),
	os      = require('os'),
	exec    = require('child_process').exec;

module.exports = function(command, opt) {
	// Assign default options if one is not supplied
	opt = opt || {};
	opt = {

		// general settings
		silent:             opt.silent              || false,
		debug:              opt.debug               || false,
		clear:              opt.clear               || false,
		dryRun:             opt.dryRun               || false,

		// code coverage options
		coverageClover:     opt.coverageClover      || '',
		coverageCrap4j:     opt.coverageCrap4j      || '',
		coverageHtml:       opt.coverageHtml        || '',
		coveragePhp:        opt.coveragePhp         || '',
		coverageText:       opt.coverageText        || '',
		coverageXml:        opt.coverageXml         || '',

		// logging options
		logJunit:           opt.logJunit            || '',
		logTap:             opt.logTap              || '',
		logJson:            opt.logJson             || '',
		testdoxHtml:        opt.testdoxHtml         || '',
		testdoxText:        opt.testdoxText         || '',

		// test selection options
		filter:             opt.filter              || '',
		testClass:          opt.testClass           || '',
		testSuite:          opt.testSuite           || '',
		group:              opt.group               || '',
		excludeGroup:       opt.excludeGroup        || '',
		listGroups:         opt.listGroups          || '',
		testSuffix:         opt.testSuffix          || '',

		// test execution options
		reportUselessTests: opt.reportUselessTests || false,
		strictCoverage:     opt.strictCoverage     || false,
		disallowTestOutput: opt.disallowTestOutput || false,
		enforceTimeLimit:   opt.enforceTimeLimit   || false,
		disallowTodoTests:  opt.disallowTestOutput || false,
		strict:             opt.strict             || false,

		processIsolation:   opt.processIsolation   || false,
		noGlobalsBackup:    opt.noGlobalsBackup    || false,
		staticBackup:       opt.staticBackup       || false,

		colors:             opt.colors             || true,
		stderr:             opt.stderr             || false,
		stopOnError:        opt.stopOnError        || false,
		stopOnFailure:      opt.stopOnFailure      || false,
		stopOnRisky:        opt.stopOnRisky        || false,
		stopOnSkipped:      opt.stopOnSkipped      || false,
		stopOnIncomplete:   opt.stopOnIncomplete   || false,
		verbose:            opt.verbose            || false,

		loader:             opt.loader             || '',
		repeat:             opt.repeat             || '',
		tap:                opt.tap                || false,
		testdox:            opt.testdox            || false,
		printer:            opt.printer            || '',

		// configuration options
		bootstrap:          opt.bootstrap          || '',
		configurationFile:  opt.configurationFile  || false,
		noConfiguration:    opt.noConfiguration    || false,
		includePath:        opt.includePath        || ''

	};

	// If path to phpunit bin not supplied, use default vendor/bin path
	if (!command) {
		command = './vendor/bin/phpunit';

		// Use the backslashes on Windows
		if (os.platform() === 'win32') {
			command = command.replace(/[/]/g, '\\');
		}
	} else if (typeof command !== 'string') {
		throw new gutil.PluginError('gulp-phpunit', 'Invalid PHPUnit Binary');
	}

	var launched = false;

	return map( function(file, cb) {
		// First file triggers the command, so other files does not matter
		if (launched) {
			return cb(null, file);
		}
		launched = true;

		var cmd = opt.clear ? 'clear && ' + command : command;

		// add remaining switches

		/* code coverage */
		if(opt.coverageClover)      { cmd += ' --coverage-clover=' + opt.coverageClover; }
		if(opt.coverageCrap4j)      { cmd += ' --coverage-crap4j=' + opt.coverageCrap4j; }
		if(opt.coverageHtml)        { cmd += ' --coverage-html=' + opt.coverageHtml; }
		if(opt.coveragePhp)         { cmd += ' --coverage-php=' + opt.coveragePhp; }
		if(opt.coverageText)        { cmd += ' --coverage-text=' + opt.coverageText; }
		if(opt.coverageXml)         { cmd += ' --coverage-xml=' + opt.coverageXml; }

		/* logging options */
		if(opt.logJunit)            { cmd += ' --log-junit=' + opt.logJunit; }
		if(opt.logTap)              { cmd += ' --log-tap=' + opt.logTap; }
		if(opt.logJson)             { cmd += ' --log-json=' + opt.logJson; }
		if(opt.testdoxHtml)         { cmd += ' --testdox-html=' + opt.testdoxHtml; }
		if(opt.testdoxText)         { cmd += ' --testdox-text=' + opt.testdoxText; }

		/* test selection */
		if(opt.filter)              { cmd += ' --filter=' + opt.filter; }
		if(opt.group)               { cmd += ' --group=' + opt.group; }
		if(opt.excludeGroup)        { cmd += ' --exclude-group=' + opt.excludeGroup; }
		if(opt.listGroups)          { cmd += ' --list-groups=' + opt.listGroups; }
		if(opt.testSuffix)          { cmd += ' --test-suffix=' + opt.testSuffix; }

		/* test execution options */
		if(opt.reportUselessTests)  { cmd += ' --report-useless-tests'; }
		if(opt.strictCoverage)      { cmd += ' --strict-coverage'; }
		if(opt.disallowTestOutput)  { cmd += ' --disallow-test-output'; }
		if(opt.enforceTimeLimit)    { cmd += ' --enforce-time-limit'; }
		if(opt.disallowTodoTests)   { cmd += ' --disallow-todo-tests'; }
		if(opt.strict)              { cmd += ' --strict'; }
		if(opt.processIsolation)    { cmd += ' --process-isolation'; }
		if(opt.noGlobalsBackup)     { cmd += ' --no-globals-backup'; }
		if(opt.staticBackup)        { cmd += ' --static-backup'; }
		if(opt.colors)              { cmd += ' --colors'; }
		if(opt.stderr)              { cmd += ' --stderr'; }
		if(opt.stopOnError)         { cmd += ' --stop-on-error'; }
		if(opt.stopOnFailure)       { cmd += ' --stop-on-failure'; }
		if(opt.stopOnRisky)         { cmd += ' --stop-on-risky'; }
		if(opt.stopOnSkipped)       { cmd += ' --stop-on-skipped'; }
		if(opt.stopOnIncomplete)    { cmd += ' --stop-on-incomplete'; }
		if(opt.loader)              { cmd += ' --loader=' + opt.loader; }
		if(opt.repeat)              { cmd += ' --repeat=' + opt.repeat; }
		if(opt.tap)                 { cmd += ' --tap'; }
		if(opt.testdox)             { cmd += ' --testdox'; }
		if(opt.printer)             { cmd += ' --printer=' + opt.printer; }
		if (opt.debug)              { cmd += ' --debug'; }

		/* configuration options */
		if(opt.bootstrap)           { cmd += ' --bootstrap=' + opt.bootstrap; }
		if (opt.noConfiguration)    { cmd += ' --no-configuration'; }
		if (opt.includePath)        { cmd += ' --include-path=' + opt.includePath; }

		/* ---- FINALLY add testClass, Suite or Configuration -- */

		// after options and switches are added, then add either testClass or testSuite

		// Priority:
		// - configuration file
		// - testSuite
		// - testClass

		var skip;

		if ((opt.configurationFile) && (! skip) && (!opt.noConfiguration)){
			cmd += ' -c ' + opt.configurationFile;
			skip = true;
		}

		if ((opt.testSuite) && (! skip)) {
			cmd += ' ' + opt.testSuite;
			skip = true;
		}

		if ((opt.testClass) && (! skip)) {
			cmd += ' ' + opt.testClass;
			skip = true;
		}

		// append debug code if switch enabled
		if ((opt.debug) || (opt.dryRun)) {
			if(opt.dryRun) {
				gutil.log(gutil.colors.green('\n\n       *** Dry Run Cmd: ' + cmd  + ' ***\n'));
			} else {
				gutil.log(gutil.colors.yellow('\n\n       *** Debug Cmd: ' + cmd  + ' ***\n'));
			}
		}

		/* -- EXECUTE -- */
		if( ! opt.dryRun ) {
			exec(cmd, function (error, stdout, stderr) {
				if (!opt.silent && stderr) {
					gutil.log(stderr);
				}

				if (!opt.silent) {
					// Trim trailing cr-lf
					stdout = stdout.trim();

					if (stdout) {
						gutil.log(stdout);
					}
				}

				// call user callback if ano error occurs
				if (error) {
					if (opt.debug) {
						gutil.log(error);
					}
					cb(error, file);
				} else {
					cb(null, file);
				}

			});
		}

	});
};
<|endoftext|>


<commit_before>var Botkit = require("botkit");

// Expect a SLACK_TOKEN environment variable
var slackToken = process.env.SLACK_TOKEN;
if (!slackToken) {
    console.error("SLACK_TOKEN is required!");
    process.exit(1);
}

var controller = Botkit.slackbot();
var bot = controller.spawn({
    "token": slackToken
});

bot.startRTM(function(err, bot, payload) {
    if (err) {
        throw new Error("Could not connect to Slack");
    }
});

// Join message
controller.on("bot_channel_join", function(bot, message) {
    bot.reply(message, "I'm here!");
});

// Mention listener
controller.hears(["hello", "hi", "sup", "howdy", "yo"], ["direct_mention"], function(bot, message) {
    bot.reply(message, "sup");
});

// DM listener
controller.hears(["hello", "hi", "sup", "howdy", "yo"], ["direct_message"], function(bot, message) {
    bot.reply(message, "Wuddup.");
    bot.reply(message, "It\'s nice to talk to you directly.");
});

// Help message
controller.hears("help", ["direct_message", "direct_mention"], function(bot, message) {
    var help = "I will respond to the following messages: \n" +
        "`bot hi` for a simple message.\n" +
        "`bot convert [number]` to convert DKK to USD.\n" +
        "`bot convert from [number] [currency1] to [currency2]` to convert a currency.\n" +
        "`bot translate [words]` to translate anything into English.\n" +
        "`bot help` to see this again.";
    bot.reply(message, help);
});

// Custom scripts
// ==============

//=> Interpreter: translates Danish to English

// Expect a GOOGLE_TOKEN environment variable
var googleToken = process.env.GOOGLE_TOKEN;
if (!googleToken) {
    console.error("GOOGLE_TOKEN is required!");
    process.exit(1);
}

var googleTranslate = require("google-translate")(googleToken);

controller.hears(["^translate (.*)$"], ["direct_message", "direct_mention"], function(bot, message) {
    googleTranslate.translate(message.match[1], "da", "en", function(err, translation) {
        try {
            bot.reply(message, ":flag-dk: => " + translation.translatedText);
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});
// <=

//=> Converter: converts currencies

// Use openexchangerates.com for up-to-date rates
// Expect an OXR_TOKEN environment variable
var oxrToken = process.env.OXR_TOKEN;
if (!oxrToken) {
    console.error("OXR_TOKEN is required!");
    process.exit(1);
}

var oxr = require("open-exchange-rates"),
    fx = require("money"),
    d3Format = require("d3-format");

fx.settings = {"from": "DKK", "to": "USD"}; //! Broken
var fxSettingsIsBroken = true;

oxr.set({"app_id": oxrToken});

var format = d3Format.format(",.3s"),
    currencyPattern = /([+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]{2})?) (\w{3}) to (\w{3})/g;

// Takes a value, an input currency, and returns an output currency
controller.hears(["^convert from (.*)"], ["direct_message", "direct_mention"], function(bot, message) {
    oxr.latest(function() {
        try {
            fx.rates = oxr.rates,
            fx.base = oxr.base;

            match = currencyPattern.exec(message.match[1]);

            num = match[1],
            fromCur = match[2],
            toCur = match[3];

            result = format(fx(num).from(fromCur).to(toCur));

            bot.reply(message, result + " " + toCur);
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});

// Converts a number from USD to DKK
controller.hears(["^convert (.*)$"], ["direct_message", "direct_mention"], function(bot, message) {
    oxr.latest(function() {
        try {
            fx.rates = oxr.rates,
            fx.base = oxr.base;

            // fromCur = "",
            // toCur = "",
            num = message.match[1];

            if (fxSettingsIsBroken) { // Long conversion with explicit currencies
                fromCur = fx.settings.from, //  message.from || fx.settings.from
                toCur = fx.settings.to; // message.to || fx.settings.to;

                result = format(fx(num).from(fromCur).to(toCur));

                bot.reply(message, result + " " + toCur);
            }
            else { // Shorthand conversion with implied currencies
                bot.reply(message, (fx(num)));
            }
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});
// <=

// This goes by the end of the file; it works as an "else" function for listener events.
controller.hears(".*", ["direct_message", "direct_mention"], function(bot, message) {
    bot.reply(message, "Sorry <@" + message.user + ">, I don\'t understand. \n");
});
<commit_msg>Convert currency arguments to uppercase<commit_after>var Botkit = require("botkit");

// Expect a SLACK_TOKEN environment variable
var slackToken = process.env.SLACK_TOKEN;
if (!slackToken) {
    console.error("SLACK_TOKEN is required!");
    process.exit(1);
}

var controller = Botkit.slackbot();
var bot = controller.spawn({
    "token": slackToken
});

bot.startRTM(function(err, bot, payload) {
    if (err) {
        throw new Error("Could not connect to Slack");
    }
});

// Join message
controller.on("bot_channel_join", function(bot, message) {
    bot.reply(message, "I'm here!");
});

// Mention listener
controller.hears(["hello", "hi", "sup", "howdy", "yo"], ["direct_mention"], function(bot, message) {
    bot.reply(message, "sup");
});

// DM listener
controller.hears(["hello", "hi", "sup", "howdy", "yo"], ["direct_message"], function(bot, message) {
    bot.reply(message, "Wuddup.");
    bot.reply(message, "It\'s nice to talk to you directly.");
});

// Help message
controller.hears("help", ["direct_message", "direct_mention"], function(bot, message) {
    var help = "I will respond to the following messages: \n" +
        "`bot hi` for a simple message.\n" +
        "`bot convert [number]` to convert DKK to USD.\n" +
        "`bot convert from [number] [currency1] to [currency2]` to convert a currency.\n" +
        "`bot translate [words]` to translate anything into English.\n" +
        "`bot help` to see this again.";
    bot.reply(message, help);
});

// Custom scripts
// ==============

//=> Interpreter: translates Danish to English

// Expect a GOOGLE_TOKEN environment variable
var googleToken = process.env.GOOGLE_TOKEN;
if (!googleToken) {
    console.error("GOOGLE_TOKEN is required!");
    process.exit(1);
}

var googleTranslate = require("google-translate")(googleToken);

controller.hears(["^translate (.*)$"], ["direct_message", "direct_mention"], function(bot, message) {
    googleTranslate.translate(message.match[1], "da", "en", function(err, translation) {
        try {
            bot.reply(message, ":flag-dk: => " + translation.translatedText);
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});
// <=

//=> Converter: converts currencies

// Use openexchangerates.com for up-to-date rates
// Expect an OXR_TOKEN environment variable
var oxrToken = process.env.OXR_TOKEN;
if (!oxrToken) {
    console.error("OXR_TOKEN is required!");
    process.exit(1);
}

var oxr = require("open-exchange-rates"),
    fx = require("money"),
    d3Format = require("d3-format");

fx.settings = {"from": "DKK", "to": "USD"}; //! Broken
var fxSettingsIsBroken = true;

oxr.set({"app_id": oxrToken});

var format = d3Format.format(",.3s"),
    currencyPattern = /([+-]?[0-9]{1,3}(?:,?[0-9]{3})*(?:\.[0-9]{2})?) (\w{3}) to (\w{3})/g;

// Takes a value, an input currency, and returns an output currency
controller.hears(["^convert from (.*)"], ["direct_message", "direct_mention"], function(bot, message) {
    oxr.latest(function() {
        try {
            fx.rates = oxr.rates,
            fx.base = oxr.base;

            match = currencyPattern.exec(message.match[1]);

            // Uppercase currencies are required---apparently
            num = match[1],
            fromCur = match[2].toUpperCase(),
            toCur = match[3].toUpperCase();

            result = format(fx(num).from(fromCur).to(toCur));

            bot.reply(message, result + " " + toCur);
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});

// Converts a number from USD to DKK
controller.hears(["^convert (.*)$"], ["direct_message", "direct_mention"], function(bot, message) {
    oxr.latest(function() {
        try {
            fx.rates = oxr.rates,
            fx.base = oxr.base;

            // fromCur = "",
            // toCur = "",
            num = message.match[1];

            if (fxSettingsIsBroken) { // Long conversion with explicit currencies
                fromCur = fx.settings.from, //  message.from || fx.settings.from
                toCur = fx.settings.to; // message.to || fx.settings.to;

                result = format(fx(num).from(fromCur).to(toCur));

                bot.reply(message, result + " " + toCur);
            }
            else { // Shorthand conversion with implied currencies
                bot.reply(message, (fx(num)));
            }
        }
        catch (err) {
            bot.reply(message, err);
        }
    });
});
// <=

// This goes by the end of the file; it works as an "else" function for listener events.
controller.hears(".*", ["direct_message", "direct_mention"], function(bot, message) {
    bot.reply(message, "Sorry <@" + message.user + ">, I don\'t understand. \n");
});
<|endoftext|>


<commit_before>"use strict";

const assert = require("assert");
const deasync = require("deasync");
const Redlock = require("redlock");

function awaitPromise(p) {
	let done = false;
	let ret;
	let err;
	p.then(arg => {
		done = true;
		ret = arg;
	})
	.catch(e => {
		done = true;
		err = e;
	});
	deasync.loopWhile(() => !done);
	if (err) throw err;
	return ret;
}

function promisableGet(opt, rootKey, permissive = false) {
	return new Promise(
		(resolve, reject) => opt.redis.hget(opt.namespace, rootKey, (err, json) => {
			if (err) {
				reject(err);
				return;
			}
			try {
				if (permissive && json === "undefined") return undefined;
				resolve(JSON.parse(json) || {});
			} catch (e) {
				reject(e);
			}
		})
	);
}

function promisableSet(opt, key, val) {
	assert.notStrictEqual(typeof val, "undefined");
	let json = JSON.stringify(val);
	try {
		JSON.parse(json);
	} catch (e) {
		json = "{}";
	}
	return new Promise(
		(resolve, reject) => opt.redis.hset(opt.namespace, key, json, err => {
			if (err)
				reject(err);
			else
				resolve();
		})
	);
}

function promisableModify(opt, rootKey, tree, fun) {
	// Yes, it's ugly, but it's needed to keep variables around
	return opt.redlock.lock(rootKey, opt.lockTTL)
		.then(lock => promisableGet(opt, rootKey)
			.then(rootVal => nestedApply(rootVal, tree, fun))
			.then(({newObj, ret}) => promisableSet(opt, rootKey, newObj)
				.then(() => lock.unlock())
				.then(() => ret)
		)
	);
}

// Abstracted version of http://stackoverflow.com/a/18937118.
// Note: this _does not work_ with nested set
function nestedApply(obj, path, fun) {
	if (path.length === 0) {
		const _ret = fun(obj);
		return {
			ret: _ret,
			newObj: obj
		};
	}
	// https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments
	let newObj = obj;
	const last = path.pop();
	for (const elem of path) {
		if (!newObj[elem])
			newObj[elem] = {};
		newObj = newObj[elem];
	}
	const ret = fun(newObj[last]);
	return {
		ret,
		newObj
	};
}

function nestedSet(obj, path, value) {
	assert.notStrictEqual(typeof value, "undefined");
	let newObj = obj;
	const last = path.pop();
	for (const elem of path) {
		if (!newObj[elem])
			newObj[elem] = {};
		newObj = newObj[elem];
	}

	return (newObj[last] = value);
}

/* Gets a "root value" from Redis (i.e. one stored in a Redis hash),
 * deserializes it from JSON, and returns a promise.
 * Also contains a "tree" property, which is used when navigating the
 * deserialized object.
 */
function RedisWrapper(opt, key) {
	return {
		_promise: new Promise(
			(resolve, reject) => opt.redis.hget(
				opt.namespace,
				key,
				(err, json) => {
					if (err) {
						reject(err);
						return;
					}
					try {
						const val = JSON.parse(json);
						resolve(val);
					} catch (e) {
						reject(e);
					}
				}
			),
			[]
		),
		tree: []
	};
}

function ProxiedWrapper(opt, promise, rootKey) {
	return new Proxy(
		promise,
		{
			get: (obj, key) => {
				// _value value
				if (opt.deasynced && key === "_value") {
					return awaitPromise(obj._promise.then(value => {
						while (obj.tree.length > 0) {
							const curKey = obj.tree.shift();
							value = value[curKey];
						}
						return value;
					}));
				}
				// _promise property
				if (!opt.deasynced && key === "_promise") {
					return obj._promise.then(value => {
						while (obj.tree.length > 0) {
							const curKey = obj.tree.shift();
							value = value[curKey];
						}
						return value;
					});
				}
				// Standard stuff
				if (typeof key === "symbol" || key === "inspect" || key in obj)
					return obj[key];
				// .set special Promise
				if (!opt.deasynced && key === "set") {
					return val => promisableGet(opt, rootKey, true)
						.then(rootValue => {
							let ret;
							if (obj.tree.length > 0) {
								ret = nestedSet(rootValue, obj.tree, val);
							} else {
								ret = (rootValue = val);
							}
							return promisableSet(opt, rootKey, rootValue).then(() => ret);
						});
				}
				// .delete special Promise
				if (!opt.deasynced && key === "delete")
					return prop => promisableModify(opt, rootKey, obj.tree, item => delete item[prop]);
				// .in special Promise
				if (!opt.deasynced && key === "in")
					return prop => promisableModify(opt, rootKey, obj.tree, item => prop in item);

				const forceFunc = /^__func_/.test(key);
				const forceProp = /^__prop_/.test(key);
				/*
				This is complex, but rather elegant.
				If the user is calling an Array method (eg. push), it returns a promise.
				This promise walks the `rootKey` object using `obj.tree` as a path, and
				applies the given function passing the same arguments.

					| Eg. if `key` is `"push"` and `rootKey` is
					|
					|     {
					|         a: {
					|             b: {
					|                 c: [1]
					|             }
					|         }
					|     }
					|
					| and `obj.tree` is `["a", "b", "c"]`, it will return a function
					| that navigates the object until `a.b.c` (i.e. `[1]`), and will
					| call
					|
					|     item => item["push"].apply(item, arguments)
					|
					| on that. So, when the function is actually executed (eg.
					| `db.foo.a.b.c.push(10)`), it will call `item => item.push(10)`.
				 */
				if (forceFunc || (!forceProp && key in Array.prototype)) {
					if (forceFunc)
						key = key.replace(/^__func_/i, "");
					return function() {
						const promise = promisableModify(opt, rootKey, obj.tree, item => item[key].apply(item, arguments));
						if (opt.deasynced)
							return awaitPromise(promise);
						return promise;
					};
				}
				if (forceProp)
					key = key.replace(/^__prop_/i, "");
				obj.tree.push(key);
				return new ProxiedWrapper(opt, obj, rootKey);
			},
			set: (obj, prop, val) => {
				if (!opt.deasynced)
					throw new Error("Can't assign values to Rebridge objects, use the .set() Promise instead");
				obj.tree.push(prop);
				awaitPromise(promisableGet(opt, rootKey, true)
					.then(rootValue => {
						if (obj.tree.length > 0) {
							nestedSet(rootValue, obj.tree, val);
						} else {
							rootValue = val;
						}
						promisableSet(opt, rootKey, rootValue);
					}));
				return true;
			},
			has: (obj, prop) => {
				if (!opt.deasynced)
					throw new Error("The `in` operator isn't supported for Rebridge objects, use the .in() Promise instead.");
				return awaitPromise(promisableModify(opt, rootKey, obj.tree, item => prop in item));
			},
			deleteProperty: (obj, prop) => {
				if (!opt.deasynced)
					throw new Error("The `delete` operator isn't supported for Rebridge objects, use the .delete() Promise instead");
				awaitPromise(promisableModify(opt, rootKey, obj.tree, item => delete item[prop]));
				return true;
			}
		}
	);
}

function RootProxiedWrapper(opt, targetObj) {
	return new Proxy(
		targetObj,
		{
			get: (obj, key) => {
				if (key in obj) {
					return obj[key];
				}
				assert.deepEqual(typeof key, "string");
				if (key === "set")
					throw new Error("You can't call .set on the root object. Syntax: db.foo.set(bar)");
				if (!opt.deasynced && key === "in")
					return key => new Promise(
						(resolve, reject) => opt.redis.hexists(
							opt.namespace,
							key,
							(err, val) => {
								if (err)
									reject(err);
								else
									resolve(val === 1);
							}
						)
					);
				if (key === "delete")
					return key => new Promise(
						(resolve, reject) => opt.redis.hdel(
							opt.namespace,
							key,
							err => {
								if (err)
									reject(err);
								else
									resolve(true);
							}
						)
					);
				return new ProxiedWrapper(opt, new RedisWrapper(opt, key), key);
			},
			set: (target, prop, val) => {
				if (!opt.deasynced)
					throw new Error("Can't assign values to Rebridge objects, use the .set() Promise instead");
				let done = false;
				let err = null;
				opt.redis.hset(opt.namespace, prop, JSON.stringify(val), e => {
					done = true;
					err = e;
				});
				deasync.loopWhile(() => !done);
				if (err) throw err;
				return true;
			},
			has: (target, prop) => {
				if (!opt.deasynced)
					throw new Error("The `in` operator isn't supported for Rebridge objects, use the .in() Promise instead");
				let done = false;
				let err;
				let ret;
				opt.redis.hexists(opt.namespace, prop, (e, val) => {
					done = true;
					err = e;
					ret = val;
				});
				deasync.loopWhile(() => !done);
				if (err) throw err;
				return ret;
			},
			deleteProperty: (target, prop) => {
				if (!opt.deasynced)
					throw new Error("The `delete` operator isn't supported for Rebridge objects, use the .delete() Promise isntead");
				let done = false;
				let err;
				opt.redis.hdel(opt.namespace, prop, e => {
					done = true;
					err = e;
				});
				deasync.loopWhile(() => !done);
				if (err) throw err;
				return true;
			}
		}
	);
}

// Catches "reads" of db.foo, and returns a wrapper around the deserialized value from Redis.
class Rebridge {
	constructor(client, {
		lock = true,
		lockTTL = 1000,
		clients = [client],
		mode = "promise",
		namespace = "rebridge"
	} = {}) {
		const deasynced = mode === "deasync";
		const redis = client;
		let redlock;
		if (lock)
			redlock = new Redlock(clients);
		else // Use a dummy lock
			redlock = {
				lock: () => Promise.resolve({
					unlock: () => Promise.resolve()
				})
			};
		const opt = {
			deasynced,
			redis,
			redlock,
			lockTTL,
			namespace
		};
		return new RootProxiedWrapper(opt, {});
	}
}

module.exports = Rebridge;<commit_msg>Add support for custom serializer/deserializer<commit_after>"use strict";

const assert = require("assert");
const deasync = require("deasync");
const Redlock = require("redlock");

function awaitPromise(p) {
	let done = false;
	let ret;
	let err;
	p.then(arg => {
		done = true;
		ret = arg;
	})
	.catch(e => {
		done = true;
		err = e;
	});
	deasync.loopWhile(() => !done);
	if (err) throw err;
	return ret;
}

function promisableGet(opt, rootKey, permissive = false) {
	return new Promise(
		(resolve, reject) => opt.redis.hget(opt.namespace, rootKey, (err, json) => {
			if (err) {
				reject(err);
				return;
			}
			try {
				if (permissive && json === "undefined") return undefined;
				resolve(opt.deserialize(json) || {});
			} catch (e) {
				reject(e);
			}
		})
	);
}

function promisableSet(opt, key, val) {
	assert.notStrictEqual(typeof val, "undefined");
	let json = opt.serialize(val);
	try {
		opt.deserialize(json);
	} catch (e) {
		json = "{}";
	}
	return new Promise(
		(resolve, reject) => opt.redis.hset(opt.namespace, key, json, err => {
			if (err)
				reject(err);
			else
				resolve();
		})
	);
}

function promisableModify(opt, rootKey, tree, fun) {
	// Yes, it's ugly, but it's needed to keep variables around
	return opt.redlock.lock(rootKey, opt.lockTTL)
		.then(lock => promisableGet(opt, rootKey)
			.then(rootVal => nestedApply(rootVal, tree, fun))
			.then(({newObj, ret}) => promisableSet(opt, rootKey, newObj)
				.then(() => lock.unlock())
				.then(() => ret)
		)
	);
}

// Abstracted version of http://stackoverflow.com/a/18937118.
// Note: this _does not work_ with nested set
function nestedApply(obj, path, fun) {
	if (path.length === 0) {
		const _ret = fun(obj);
		return {
			ret: _ret,
			newObj: obj
		};
	}
	// https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments
	let newObj = obj;
	const last = path.pop();
	for (const elem of path) {
		if (!newObj[elem])
			newObj[elem] = {};
		newObj = newObj[elem];
	}
	const ret = fun(newObj[last]);
	return {
		ret,
		newObj
	};
}

function nestedSet(obj, path, value) {
	assert.notStrictEqual(typeof value, "undefined");
	let newObj = obj;
	const last = path.pop();
	for (const elem of path) {
		if (!newObj[elem])
			newObj[elem] = {};
		newObj = newObj[elem];
	}

	return (newObj[last] = value);
}

/* Gets a "root value" from Redis (i.e. one stored in a Redis hash),
 * deserializes it from JSON, and returns a promise.
 * Also contains a "tree" property, which is used when navigating the
 * deserialized object.
 */
function RedisWrapper(opt, key) {
	return {
		_promise: new Promise(
			(resolve, reject) => opt.redis.hget(
				opt.namespace,
				key,
				(err, json) => {
					if (err) {
						reject(err);
						return;
					}
					try {
						const val = opt.deserialize(json);
						resolve(val);
					} catch (e) {
						reject(e);
					}
				}
			),
			[]
		),
		tree: []
	};
}

function ProxiedWrapper(opt, promise, rootKey) {
	return new Proxy(
		promise,
		{
			get: (obj, key) => {
				// _value value
				if (opt.deasynced && key === "_value") {
					return awaitPromise(obj._promise.then(value => {
						while (obj.tree.length > 0) {
							const curKey = obj.tree.shift();
							value = value[curKey];
						}
						return value;
					}));
				}
				// _promise property
				if (!opt.deasynced && key === "_promise") {
					return obj._promise.then(value => {
						while (obj.tree.length > 0) {
							const curKey = obj.tree.shift();
							value = value[curKey];
						}
						return value;
					});
				}
				// Standard stuff
				if (typeof key === "symbol" || key === "inspect" || key in obj)
					return obj[key];
				// .set special Promise
				if (!opt.deasynced && key === "set") {
					return val => promisableGet(opt, rootKey, true)
						.then(rootValue => {
							let ret;
							if (obj.tree.length > 0) {
								ret = nestedSet(rootValue, obj.tree, val);
							} else {
								ret = (rootValue = val);
							}
							return promisableSet(opt, rootKey, rootValue).then(() => ret);
						});
				}
				// .delete special Promise
				if (!opt.deasynced && key === "delete")
					return prop => promisableModify(opt, rootKey, obj.tree, item => delete item[prop]);
				// .in special Promise
				if (!opt.deasynced && key === "in")
					return prop => promisableModify(opt, rootKey, obj.tree, item => prop in item);

				const forceFunc = /^__func_/.test(key);
				const forceProp = /^__prop_/.test(key);
				/*
				This is complex, but rather elegant.
				If the user is calling an Array method (eg. push), it returns a promise.
				This promise walks the `rootKey` object using `obj.tree` as a path, and
				applies the given function passing the same arguments.

					| Eg. if `key` is `"push"` and `rootKey` is
					|
					|     {
					|         a: {
					|             b: {
					|                 c: [1]
					|             }
					|         }
					|     }
					|
					| and `obj.tree` is `["a", "b", "c"]`, it will return a function
					| that navigates the object until `a.b.c` (i.e. `[1]`), and will
					| call
					|
					|     item => item["push"].apply(item, arguments)
					|
					| on that. So, when the function is actually executed (eg.
					| `db.foo.a.b.c.push(10)`), it will call `item => item.push(10)`.
				 */
				if (forceFunc || (!forceProp && key in Array.prototype)) {
					if (forceFunc)
						key = key.replace(/^__func_/i, "");
					return function() {
						const promise = promisableModify(opt, rootKey, obj.tree, item => item[key].apply(item, arguments));
						if (opt.deasynced)
							return awaitPromise(promise);
						return promise;
					};
				}
				if (forceProp)
					key = key.replace(/^__prop_/i, "");
				obj.tree.push(key);
				return new ProxiedWrapper(opt, obj, rootKey);
			},
			set: (obj, prop, val) => {
				if (!opt.deasynced)
					throw new Error("Can't assign values to Rebridge objects, use the .set() Promise instead");
				obj.tree.push(prop);
				awaitPromise(promisableGet(opt, rootKey, true)
					.then(rootValue => {
						if (obj.tree.length > 0) {
							nestedSet(rootValue, obj.tree, val);
						} else {
							rootValue = val;
						}
						promisableSet(opt, rootKey, rootValue);
					}));
				return true;
			},
			has: (obj, prop) => {
				if (!opt.deasynced)
					throw new Error("The `in` operator isn't supported for Rebridge objects, use the .in() Promise instead.");
				return awaitPromise(promisableModify(opt, rootKey, obj.tree, item => prop in item));
			},
			deleteProperty: (obj, prop) => {
				if (!opt.deasynced)
					throw new Error("The `delete` operator isn't supported for Rebridge objects, use the .delete() Promise instead");
				awaitPromise(promisableModify(opt, rootKey, obj.tree, item => delete item[prop]));
				return true;
			}
		}
	);
}

function RootProxiedWrapper(opt, targetObj) {
	return new Proxy(
		targetObj,
		{
			get: (obj, key) => {
				if (key in obj) {
					return obj[key];
				}
				assert.deepEqual(typeof key, "string");
				if (key === "set")
					throw new Error("You can't call .set on the root object. Syntax: db.foo.set(bar)");
				if (!opt.deasynced && key === "in")
					return key => new Promise(
						(resolve, reject) => opt.redis.hexists(
							opt.namespace,
							key,
							(err, val) => {
								if (err)
									reject(err);
								else
									resolve(val === 1);
							}
						)
					);
				if (key === "delete")
					return key => new Promise(
						(resolve, reject) => opt.redis.hdel(
							opt.namespace,
							key,
							err => {
								if (err)
									reject(err);
								else
									resolve(true);
							}
						)
					);
				return new ProxiedWrapper(opt, new RedisWrapper(opt, key), key);
			},
			set: (target, prop, val) => {
				if (!opt.deasynced)
					throw new Error("Can't assign values to Rebridge objects, use the .set() Promise instead");
				let done = false;
				let err = null;
				opt.redis.hset(opt.namespace, prop, opt.serialize(val), e => {
					done = true;
					err = e;
				});
				deasync.loopWhile(() => !done);
				if (err) throw err;
				return true;
			},
			has: (target, prop) => {
				if (!opt.deasynced)
					throw new Error("The `in` operator isn't supported for Rebridge objects, use the .in() Promise instead");
				let done = false;
				let err;
				let ret;
				opt.redis.hexists(opt.namespace, prop, (e, val) => {
					done = true;
					err = e;
					ret = val;
				});
				deasync.loopWhile(() => !done);
				if (err) throw err;
				return ret;
			},
			deleteProperty: (target, prop) => {
				if (!opt.deasynced)
					throw new Error("The `delete` operator isn't supported for Rebridge objects, use the .delete() Promise isntead");
				let done = false;
				let err;
				opt.redis.hdel(opt.namespace, prop, e => {
					done = true;
					err = e;
				});
				deasync.loopWhile(() => !done);
				if (err) throw err;
				return true;
			}
		}
	);
}

// Catches "reads" of db.foo, and returns a wrapper around the deserialized value from Redis.
class Rebridge {
	constructor(client, {
		lock = true,
		lockTTL = 1000,
		clients = [client],
		mode = "promise",
		namespace = "rebridge",
		serialize = JSON.stringify,
		deserialize = JSON.parse
	} = {}) {
		const deasynced = mode === "deasync";
		const redis = client;
		let redlock;
		if (lock)
			redlock = new Redlock(clients);
		else // Use a dummy lock
			redlock = {
				lock: () => Promise.resolve({
					unlock: () => Promise.resolve()
				})
			};
		const opt = {
			deasynced,
			redis,
			redlock,
			lockTTL,
			namespace,
			serialize,
			deserialize
		};
		return new RootProxiedWrapper(opt, {});
	}
}

module.exports = Rebridge;<|endoftext|>


<commit_before>var os = require('os');
var path = require('path');
var fs = require('fs');
var swig = require('swig');

var KarmaDataurlReporter = function(baseReporterDecorator, config, logger, helper, formatError, emitter) {

  var reporterConfig = config.dataurlReporter || {};
  var canvases = {};
  var results = [];

  var pkgName = reporterConfig.suite || '';

  var outputFile = helper.normalizeWinPath(path.resolve(config.basePath, reporterConfig.outputFile
      || 'dataurl-results.html'));

  var templateFile = helper.normalizeWinPath(path.resolve(config.basePath, reporterConfig.templateFile
      || __dirname+"/defaulttemplatereport.html"));

  var reportTitle = reporterConfig.reportTitle || 'CANVASES';

  var html = "";
  var pendingFileWritings = 0;
  var fileWritingFinished = function() {};
  var allMessages = [];

  baseReporterDecorator(this);

  this.adapters = [function(msg) {
    allMessages.push(msg);
  }];

  // étend la classe Browser pour attraper les dataurl avant qu’elles n’arrivent dans
  // la sortie console !!!
  this.onBrowserStart = function(browser) {
        var baseInfo = browser.onInfo;
        browser.onInfo = function(info) {
        if(info.log.dataurl) {
          var dto = info.log;
          if(!canvases[dto.id]) {
            canvases[dto.id] = {result:{}, canvases: []};
          }
          canvases[dto.id].canvases.push(dto);
          info = {log:'CanvasReporter:: reporting dataurl for ' + dto.id + ' (' + browser.name + ')', type:'info'};
        }
        baseInfo.call(browser, info);
      };
  };

  this.specSuccess = this.specSkipped = this.specFailure = function(browser, result) {
    results.push(result);
  };

  this.onRunComplete = function() {
    pendingFileWritings++;

    results.forEach(function(result) {
      if(!!canvases[result.id]) {
          canvases[result.id].result = result;
      }
    });

    var html = swig.renderFile(templateFile, {
      title: reportTitle,
      tests: canvases
    });

    canvases ={};
    results = [];

    helper.mkdirIfNotExists(path.dirname(outputFile), function() {
      fs.writeFile(outputFile, html, function(err) {
        if (err) {
          log.warn('Cannot write HTML\n\t' + err.message);
        } else {
          log.debug('Dataurls written to "%s".', outputFile);
        }

        if (!--pendingFileWritings) {
          fileWritingFinished();
        }
      });
    });
  };

  // wait for writing all the xml files, before exiting
  this.onExit = function(done) {
    if (pendingFileWritings) {
      fileWritingFinished = done;
    } else {
      done();
    }
  };
};

KarmaDataurlReporter.$inject = ['baseReporterDecorator', 'config', 'logger', 'helper', 'formatError', 'emitter'];

module.exports = {
  'reporter:dataurlReporter': ['type', KarmaDataurlReporter]
};
<commit_msg>karma renaming<commit_after>var os = require('os');
var path = require('path');
var fs = require('fs');
var swig = require('swig');

var KarmaDataurlReporter = function(baseReporterDecorator, config, logger, helper, formatError, emitter) {

  var reporterConfig = config.dataurlReporter || {};
  var canvases = {};
  var results = [];

  var pkgName = reporterConfig.suite || '';

  var outputFile = helper.normalizeWinPath(path.resolve(config.basePath, reporterConfig.outputFile
      || 'dataurl-results.html'));

  var templateFile = helper.normalizeWinPath(path.resolve(config.basePath, reporterConfig.templateFile
      || __dirname+"/defaulttemplatereport.html"));

  var reportTitle = reporterConfig.reportTitle || 'CANVASES';

  var html = "";
  var pendingFileWritings = 0;
  var fileWritingFinished = function() {};
  var allMessages = [];

  baseReporterDecorator(this);

  this.adapters = [function(msg) {
    allMessages.push(msg);
  }];

  // étend la classe Browser pour attraper les dataurl avant qu’elles n’arrivent dans
  // la sortie console !!!
  this.onBrowserStart = function(browser) {
        var baseInfo = browser.onInfo;
        browser.onInfo = function(info) {
        if(info.log.dataurl) {
          var dto = info.log;
          if(!canvases[dto.id]) {
            canvases[dto.id] = {result:{}, canvases: []};
          }
          canvases[dto.id].canvases.push(dto);
          info = {log:'CanvasReporter:: reporting dataurl for ' + dto.id + ' (' + browser.name + ')', type:'info'};
        }
        baseInfo.call(browser, info);
      };
  };

  this.specSuccess = this.specSkipped = this.specFailure = function(browser, result) {
    results.push(result);
  };

  this.onRunComplete = function() {
    pendingFileWritings++;

    results.forEach(function(result) {
      if(!!canvases[result.id]) {
          canvases[result.id].result = result;
      }
    });

    var html = swig.renderFile(templateFile, {
      title: reportTitle,
      tests: canvases
    });

    canvases ={};
    results = [];

    helper.mkdirIfNotExists(path.dirname(outputFile), function() {
      fs.writeFile(outputFile, html, function(err) {
        if (err) {
          log.warn('Cannot write HTML\n\t' + err.message);
        } else {
          log.debug('Dataurls written to "%s".', outputFile);
        }

        if (!--pendingFileWritings) {
          fileWritingFinished();
        }
      });
    });
  };

  // wait for writing all the xml files, before exiting
  this.onExit = function(done) {
    if (pendingFileWritings) {
      fileWritingFinished = done;
    } else {
      done();
    }
  };
};

KarmaDataurlReporter.$inject = ['baseReporterDecorator', 'config', 'logger', 'helper', 'formatError', 'emitter'];

module.exports = {
  'reporter:dataurl': ['type', KarmaDataurlReporter]
};
<|endoftext|>


<commit_before>'use strict';

const chalk = require('chalk');
const fs = require('fs');
const pluralize = require('pluralize');
const CLIEngine = require('eslint').CLIEngine;

const configFilePrefix = '.eslintrc';

class ESLinter {
  constructor(brunchConfig) {
    this.config = brunchConfig || {};
    const config = brunchConfig.plugins && brunchConfig.plugins.eslint || {};
    this.warnOnly = config.warnOnly != null ? config.warnOnly : true;
    this.pattern = config.pattern || /^app\/.*\.js?$/;

    var useConfig;
    try {
      useConfig = fs.readdirSync(process.cwd())
        .filter((filename) => filename.startsWith(configFilePrefix))
        .filter((configFile) => {
          try {
            return fs.statSync(configFile).isFile();
          } catch (_error) {
            const e = _error.toString().replace('Error: ENOENT, ', '');
            console.warn(`${configFile} read error: ${e}`);
            return false;
          }
        })
        .length > 0;
    } catch (_error) {
      useConfig = false;
    }

    if (useConfig === false) {
      console.warn(`no usable .eslintrc.* file can be found.\nESLint will run with default options.`);
    }
    const engineConfig = { useEslintrc: useConfig };

    this.linter = new CLIEngine(engineConfig);
  }

  lint(data, path) {
    const result = this.linter.executeOnText(data, path).results[0];
    const errorCount = result.errorCount;
    if (errorCount === 0) {
      return Promise.resolve();
    }
    const errorMsg = result.messages.map(error => {
      return `${chalk.bold(error.message)} (${error.line}:${error.column})`;
    });
    errorMsg.unshift(`ESLint detected ${errorCount} ${(pluralize('problem', errorCount))}:`);
    let msg = errorMsg.join('\n');
    if (this.warnOnly) {
      msg = `warn: ${msg}`;
    }
    return (msg ? Promise.reject(msg) : Promise.resolve());
  }
}

ESLinter.prototype.brunchPlugin = true;
ESLinter.prototype.type = 'javascript';
ESLinter.prototype.extension = 'js';

module.exports = ESLinter;
<commit_msg>Support Windows with default pattern<commit_after>'use strict';

const chalk = require('chalk');
const fs = require('fs');
const pluralize = require('pluralize');
const CLIEngine = require('eslint').CLIEngine;

const configFilePrefix = '.eslintrc';

class ESLinter {
  constructor(brunchConfig) {
    this.config = brunchConfig || {};
    const config = brunchConfig.plugins && brunchConfig.plugins.eslint || {};
    this.warnOnly = config.warnOnly != null ? config.warnOnly : true;
    this.pattern = config.pattern || /^app[\/\\].*\.js?$/;

    var useConfig;
    try {
      useConfig = fs.readdirSync(process.cwd())
        .filter((filename) => filename.startsWith(configFilePrefix))
        .filter((configFile) => {
          try {
            return fs.statSync(configFile).isFile();
          } catch (_error) {
            const e = _error.toString().replace('Error: ENOENT, ', '');
            console.warn(`${configFile} read error: ${e}`);
            return false;
          }
        })
        .length > 0;
    } catch (_error) {
      useConfig = false;
    }

    if (useConfig === false) {
      console.warn(`no usable .eslintrc.* file can be found.\nESLint will run with default options.`);
    }
    const engineConfig = { useEslintrc: useConfig };

    this.linter = new CLIEngine(engineConfig);
  }

  lint(data, path) {
    const result = this.linter.executeOnText(data, path).results[0];
    const errorCount = result.errorCount;
    if (errorCount === 0) {
      return Promise.resolve();
    }
    const errorMsg = result.messages.map(error => {
      return `${chalk.bold(error.message)} (${error.line}:${error.column})`;
    });
    errorMsg.unshift(`ESLint detected ${errorCount} ${(pluralize('problem', errorCount))}:`);
    let msg = errorMsg.join('\n');
    if (this.warnOnly) {
      msg = `warn: ${msg}`;
    }
    return (msg ? Promise.reject(msg) : Promise.resolve());
  }
}

ESLinter.prototype.brunchPlugin = true;
ESLinter.prototype.type = 'javascript';
ESLinter.prototype.extension = 'js';

module.exports = ESLinter;
<|endoftext|>


<commit_before>var TimeService = require('./lib/time');

module.exports = TimeService;
<commit_msg>Fix index<commit_after>var EventService = require('./lib/event');

module.exports = EventService;
<|endoftext|>


<commit_before>require('dotenv').config();
require('./util/checkEnv');

const os = require('os');
const fs = require('fs');
const https = require('https');
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const chalk = require('chalk');
const helmet = require('helmet');

const checkAuthToken = require('./util/checkAuthToken');
const lightsRouter = require('./endpoints/lights');

const app = express();
const apiRouter = express.Router(); // eslint-disable-line new-cap

const appPort = process.env.PORT || process.argv[2] || 8080;
const cert = {
    key: fs.readFileSync('./sslcert/key.pem'),
    cert: fs.readFileSync('./sslcert/cert.pem')
};

app.use(bodyParser.json());
app.use(morgan('common'));
app.use(helmet());

app.get('/', (req, res) => {
    res.json({
        status: 'ok',
        bridge: process.env.HUE_BRIDGE_ADDRESS
    });
});

apiRouter.use(checkAuthToken);
apiRouter.use('/lights', lightsRouter);

app.use('/api', apiRouter);

https.createServer(cert, app).listen(appPort, () => console.log(`Hue remote now listening at ${chalk.green(appPort)}...`));

if (process.argv.filter(a => a === '--letsencrypt-verify').length > 0) {
    const httpApp = express();

    console.log([
        '\n' + chalk.yellow('WARNING!'),
        'The server running in Let\'s Encrypt verification mode.',
        'It is serving any files under ' + chalk.magenta('./static') + ' without any authentication over HTTP.',
        'A restart without the ' + chalk.cyan('--letsencrypt-verify') + ' switch is suggested',
        'after verification is complete.\n'
    ].join(os.EOL));

    httpApp.use(express.static('static'));
    httpApp.listen(8080, () => console.log(`Let's Encrypt verify server running at ${chalk.green('8080')}...`));
}
<commit_msg>probably shouldn't broadcast the bridge name<commit_after>require('dotenv').config();
require('./util/checkEnv');

const os = require('os');
const fs = require('fs');
const https = require('https');
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const chalk = require('chalk');
const helmet = require('helmet');

const checkAuthToken = require('./util/checkAuthToken');
const lightsRouter = require('./endpoints/lights');

const app = express();
const apiRouter = express.Router(); // eslint-disable-line new-cap

const appPort = process.env.PORT || process.argv[2] || 8080;
const cert = {
    key: fs.readFileSync('./sslcert/key.pem'),
    cert: fs.readFileSync('./sslcert/cert.pem')
};

app.use(bodyParser.json());
app.use(morgan('common'));
app.use(helmet());

app.get('/', (req, res) => {
    res.json({
        status: 'ok',
        bridgeFound: !!process.env.HUE_BRIDGE_ADDRESS
    });
});

apiRouter.use(checkAuthToken);
apiRouter.use('/lights', lightsRouter);

app.use('/api', apiRouter);

https.createServer(cert, app).listen(appPort, () => console.log(`Hue remote now listening at ${chalk.green(appPort)}...`));

if (process.argv.filter(a => a === '--letsencrypt-verify').length > 0) {
    const httpApp = express();

    console.log([
        '\n' + chalk.yellow('WARNING!'),
        'The server running in Let\'s Encrypt verification mode.',
        'It is serving any files under ' + chalk.magenta('./static') + ' without any authentication over HTTP.',
        'A restart without the ' + chalk.cyan('--letsencrypt-verify') + ' switch is suggested',
        'after verification is complete.\n'
    ].join(os.EOL));

    httpApp.use(express.static('static'));
    httpApp.listen(8080, () => console.log(`Let's Encrypt verify server running at ${chalk.green('8080')}...`));
}
<|endoftext|>


<commit_before>var fs = require('fs');
var Steam = require('steam');
var SteamID = require('steamid');
var ByteBuffer = require('bytebuffer');

var Language = require('./language.js');
var Protos = require('./protos.js');

module.exports = GlobalOffensive;

require('util').inherits(GlobalOffensive, require('events').EventEmitter);

function GlobalOffensive(steam) {
	this._steam = steam.client || steam._client;
	this._gc = new Steam.SteamGameCoordinator(this._steam, 730);
	this.haveGCSession = false;
	this._hadGCSession = false;
	this._isInCSGO = false;

	var self = this;

	this._gc.on('message', function(header, body, callback) {
		var protobuf = !!header.proto;

		if(self._handlers[header.msg]) {
			self._handlers[header.msg].call(self, protobuf ? body : ByteBuffer.wrap(body, ByteBuffer.LITTLE_ENDIAN));
		} else {
			var msgName = type;
			for(var i in Language) {
				if(Language.hasOwnProperty(i) && Language[i] == type) {
					msgName = i;
					break;
				}
			}

			self.emit('debug', "Got unhandled GC message " + msgName + (protobuf ? " (protobuf)" : ""));
		}
	});

	// "extend" the default steam.gamesPlayed function so we can catch when CS:GO starts up
	var gamesPlayed = steam.gamesPlayed;
	steam.gamesPlayed = function(input) {
		var appids = input;

		if(appids.games_played) {
			appids = appids.games_played;
		}

		if(!(appids instanceof Array)) {
			appids = [appids];
		}

		self._isInCSGO = false;
		for(var i = 0; i < appids.length; i++) {
			if(appids[i] == 730 || appids[i].game_id == 730) {
				self._isInCSGO = true;
				break;
			}
		}

		if(self._isInCSGO) {
			if(!self.haveGCSession) {
				self._connect();
			}
		} else {
			if(self._helloInterval) {
				clearInterval(self._helloInterval);
				self._helloInterval = null;
			}

			self.haveGCSession = false;
			self._hadGCSession = false;
		}

		gamesPlayed.call(steam, appids);
	};

	this._steam.on('loggedOff', function() {
		self._isInCSGO = false;
		self._hadGCSession = self.haveGCSession;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', GlobalOffensive.GCConnectionStatus.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	this._steam.on('error', function(e) {
		self._isInCSGO = false;
		self._hadGCSession = false;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', GlobalOffensive.GCConnectionStatus.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	this._steam.on('logOnResponse', function(response) {
		if(response.eresult == Steam.EResult.OK && self._hadGCSession) {
			self._connect();
			self._hadGCSession = false;
		}
	});
}

GlobalOffensive.prototype._connect = function() {
	if(!this._isInCSGO || this._helloInterval) {
		return; // We're not in CS:GO or we're already trying to connect
	}

	var self = this;

	function sendHello() {
		if(self.haveGCSession) {
			clearInterval(self._helloInterval);
			self._helloInterval = null;
			return;
		}

		self._send(Language.ClientHello, Protos.CMsgClientHello, {});
	}

	this._helloInterval = setInterval(sendHello, 5000);
	sendHello();
};

GlobalOffensive.prototype._send = function(type, protobuf, body) {
	if(!this._steam.loggedOn) {
		return false;
	}

	var msgName = type;
	for(var i in Language) {
		if(Language.hasOwnProperty(i) && Language[i] == type) {
			msgName = i;
			break;
		}
	}

	this.emit('debug', "Sending GC message " + msgName);

	if(protobuf) {
		this._gc.send({"msg": type, "proto": {}}, (new protobuf(body)).toBuffer());
	} else {
		// This is a ByteBuffer
		this._gc.send({"msg": type}, body.flip().toBuffer());
	}

	return true;
};

GlobalOffensive.prototype.requestLiveGames = function() {
	this._send(Language.MatchListRequestCurrentLiveGames, Protos.CMsgGCCStrike15_v2_MatchListRequestCurrentLiveGames, {});
};

GlobalOffensive.prototype.inspectItem = function(owner, assetid, d, callback) {
	var match;
	if (typeof owner === 'string' && (match = owner.match(/[SM](\d+)A(\d+)D(\d+)$/))) {
		callback = assetid;
		owner = match[1];
		assetid = match[2];
		d = match[3];
	}

	var msg = {
		"paramA": assetid,
		"paramD": d,
		"paramS": 0,
		"paramM": 0
	};

	if (typeof owner === 'object') {
		owner = owner.toString();
	}

	try {
		var sid = new SteamID(owner);
		if (!sid.isValid()) {
			throw 0;
		}
		// it's a valid steamid
		msg.paramS = owner;
	} catch (e) {
		msg.paramM = owner;
	}

	this._send(Language.Client2GCEconPreviewDataBlockRequest, Protos.CMsgGCCStrike15_v2_Client2GCEconPreviewDataBlockRequest, msg);
	if (callback) {
		this.once('inspectItemInfo#' + assetid, callback);
	}
};

GlobalOffensive.prototype._handlers = {};

function coerceToLong(num, signed) {
	return typeof num === 'string' ? new ByteBuffer.Long.fromString(num, !signed, 10) : num;
}

require('./enums.js');
require('./handlers.js');
<commit_msg>Apparently some gids can be valid steamids too<commit_after>var fs = require('fs');
var Steam = require('steam');
var SteamID = require('steamid');
var ByteBuffer = require('bytebuffer');

var Language = require('./language.js');
var Protos = require('./protos.js');

module.exports = GlobalOffensive;

require('util').inherits(GlobalOffensive, require('events').EventEmitter);

function GlobalOffensive(steam) {
	this._steam = steam.client || steam._client;
	this._gc = new Steam.SteamGameCoordinator(this._steam, 730);
	this.haveGCSession = false;
	this._hadGCSession = false;
	this._isInCSGO = false;

	var self = this;

	this._gc.on('message', function(header, body, callback) {
		var protobuf = !!header.proto;

		if(self._handlers[header.msg]) {
			self._handlers[header.msg].call(self, protobuf ? body : ByteBuffer.wrap(body, ByteBuffer.LITTLE_ENDIAN));
		} else {
			var msgName = type;
			for(var i in Language) {
				if(Language.hasOwnProperty(i) && Language[i] == type) {
					msgName = i;
					break;
				}
			}

			self.emit('debug', "Got unhandled GC message " + msgName + (protobuf ? " (protobuf)" : ""));
		}
	});

	// "extend" the default steam.gamesPlayed function so we can catch when CS:GO starts up
	var gamesPlayed = steam.gamesPlayed;
	steam.gamesPlayed = function(input) {
		var appids = input;

		if(appids.games_played) {
			appids = appids.games_played;
		}

		if(!(appids instanceof Array)) {
			appids = [appids];
		}

		self._isInCSGO = false;
		for(var i = 0; i < appids.length; i++) {
			if(appids[i] == 730 || appids[i].game_id == 730) {
				self._isInCSGO = true;
				break;
			}
		}

		if(self._isInCSGO) {
			if(!self.haveGCSession) {
				self._connect();
			}
		} else {
			if(self._helloInterval) {
				clearInterval(self._helloInterval);
				self._helloInterval = null;
			}

			self.haveGCSession = false;
			self._hadGCSession = false;
		}

		gamesPlayed.call(steam, appids);
	};

	this._steam.on('loggedOff', function() {
		self._isInCSGO = false;
		self._hadGCSession = self.haveGCSession;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', GlobalOffensive.GCConnectionStatus.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	this._steam.on('error', function(e) {
		self._isInCSGO = false;
		self._hadGCSession = false;
		if(self.haveGCSession) {
			self.emit('disconnectedFromGC', GlobalOffensive.GCConnectionStatus.NO_SESSION);
			self.haveGCSession = false;
		}
	});

	this._steam.on('logOnResponse', function(response) {
		if(response.eresult == Steam.EResult.OK && self._hadGCSession) {
			self._connect();
			self._hadGCSession = false;
		}
	});
}

GlobalOffensive.prototype._connect = function() {
	if(!this._isInCSGO || this._helloInterval) {
		return; // We're not in CS:GO or we're already trying to connect
	}

	var self = this;

	function sendHello() {
		if(self.haveGCSession) {
			clearInterval(self._helloInterval);
			self._helloInterval = null;
			return;
		}

		self._send(Language.ClientHello, Protos.CMsgClientHello, {});
	}

	this._helloInterval = setInterval(sendHello, 5000);
	sendHello();
};

GlobalOffensive.prototype._send = function(type, protobuf, body) {
	if(!this._steam.loggedOn) {
		return false;
	}

	var msgName = type;
	for(var i in Language) {
		if(Language.hasOwnProperty(i) && Language[i] == type) {
			msgName = i;
			break;
		}
	}

	this.emit('debug', "Sending GC message " + msgName);

	if(protobuf) {
		this._gc.send({"msg": type, "proto": {}}, (new protobuf(body)).toBuffer());
	} else {
		// This is a ByteBuffer
		this._gc.send({"msg": type}, body.flip().toBuffer());
	}

	return true;
};

GlobalOffensive.prototype.requestLiveGames = function() {
	this._send(Language.MatchListRequestCurrentLiveGames, Protos.CMsgGCCStrike15_v2_MatchListRequestCurrentLiveGames, {});
};

GlobalOffensive.prototype.inspectItem = function(owner, assetid, d, callback) {
	var match;
	if (typeof owner === 'string' && (match = owner.match(/[SM](\d+)A(\d+)D(\d+)$/))) {
		callback = assetid;
		owner = match[1];
		assetid = match[2];
		d = match[3];
	}

	var msg = {
		"paramA": assetid,
		"paramD": d,
		"paramS": 0,
		"paramM": 0
	};

	if (typeof owner === 'object') {
		owner = owner.toString();
	}

	try {
		var sid = new SteamID(owner);
		if (!sid.isValid() || sid.universe != SteamID.Universe.PUBLIC || sid.type != SteamID.Type.INDIVIDUAL || sid.instance != SteamID.Instance.DESKTOP) {
			throw 0;
		}
		// it's a valid steamid
		msg.paramS = owner;
	} catch (e) {
		msg.paramM = owner;
	}

	this._send(Language.Client2GCEconPreviewDataBlockRequest, Protos.CMsgGCCStrike15_v2_Client2GCEconPreviewDataBlockRequest, msg);
	if (callback) {
		this.once('inspectItemInfo#' + assetid, callback);
	}
};

GlobalOffensive.prototype._handlers = {};

function coerceToLong(num, signed) {
	return typeof num === 'string' ? new ByteBuffer.Long.fromString(num, !signed, 10) : num;
}

require('./enums.js');
require('./handlers.js');
<|endoftext|>


<commit_before>var util = require('util');
var stream = require('stream');
var exec = require('child_process').exec;
var commands = require('./commands');
var configHandlers = require('./config-handlers');

util.inherits(Driver,stream);
util.inherits(Device,stream);

// not so elegant way to store all of the devices created by the driver...
var deviceList = [];

// my variables (***TODO: incorporate these into configuration)
var updateInterval = 300000; // update interval in milliseconds (for example, 600000 = 10 minutes)
var pauseAfterSetToUpdate = 7000; // time in milliseconds to wait after submitting/setting data before we try to run an update
var pauseBetweenUpdateCommands = 5; // time in seconds to wait between submitting/setting the two data requests
var ipAddressOfThermostat = "192.168.1.135"; // ip address of the thermostat
var tstatCmd = "curl -s http://" + ipAddressOfThermostat + "/tstat";

function Driver(opts,app) {
	this._app = app;
	this.opts = opts;
	app.once('client::up',function(){
		commands.forEach( this.createCommandDevice.bind(this) );
		updateDevices(app, opts);
		process.nextTick(function() {	// Once all devices are set up, establish a single update process that updates every "updateInterval" seconds
			setInterval(function() {
				updateDevices(app, opts);
			}, updateInterval);
		});
	}.bind(this));
};

Driver.prototype.createCommandDevice = function(cmd) {
	var d = new Device(this._app, cmd);
	this.emit('register', d);
	deviceList.push(d);
};

function Device(app, config) {
	app.log.info('Creating radioThermostatDriver Device : ' + config.name);
	var self = this;
	this._app = app;
	this.config = config;
	this.readable = true;
	this.writeable = config.canSet || false;
	if (this.writeable) { app.log.info('radioThermostatDriver Device ' + config.name + ' is readable and writable' ); } else app.log.info('radioThermostatDriver Device ' + config.name + ' is readable only' );
	this.V = config.vendorId || 0;
	this.D = config.deviceId;
	this.G = 'rtd' + (config.name).replace(/[^a-zA-Z0-9]/g, '');
	this.name = 'radioThermostatDriver - ' + config.name;
	// this.read();
};

function updateDevices(app, opts) {	// runs every "updateInterval" seconds
	app.log.info("Updating radioThermostatDriver Devices...");

	app.log.info(opts.ipadr); //***

	app.log.info("radioThermostatDriver executing command: " + tstatCmd);
	exec(tstatCmd, function(error, stdout, stderr) {
		app.log.info("Result of radioThermostatDriver command: " + stdout);
		if (error) {
			if (error != null) {
				app.log.warn('radioThermostatDriver : ' + this.name + ' error! - ' + error);
				return;
		  };
		};
		if (stderr) {
			if (stderr != null) {
				app.log.warn('radioThermostatDriver : ' + this.name + ' stderr! - ' + stderr);
				return;
			};
		};
		var inputString = (stdout + '');
		var thermostatData = eval ("(" + inputString + ")");
		if (!thermostatData) {
			app.log.warn('radioThermostatDriver was unable to parse data recieved. No update was made this cycle.');
			return;
		};
		deviceList.forEach(function(dev){
			app.log.info('Updating radioThermostatDriver Device: ' + dev.name);
			var parsedResult = undefined;
			(dev.config.data || []).forEach(function(fn) {
				try {
					parsedResult = fn(thermostatData);
				} catch(e) {
					parsedResult = undefined;
				}
				app.log.info(dev.name + ' - parse data: ' + inputString + ' --> ' + parsedResult);
			});
			if (parsedResult !== undefined) {
				app.log.info(dev.name + ' - emmitting data: ' + parsedResult);
				dev.emit('data', parsedResult);
			}
			else {
				app.log.info(dev.name + ' - did not emmit data!');
			};
		});
	});
};

Device.prototype.write = function(dataRcvd) {
	var app = this._app;
	var opts = this.opts;
	app.log.info("radioThermostatDriver Device " + this.name + " received data: " + dataRcvd);
	app.log.info("radioThermostatDriver Device canSet: " + this.config.canSet);
	if (this.config.canSet) {
		var stgSubmit = undefined;
		(this.config.setStg || []).forEach(function(fn) {
			try {
				stgSubmit = fn(ipAddressOfThermostat, dataRcvd);
			} catch(e) {
				stgSubmit = undefined;
			}
		});
		app.log.info("radioThermostatDriver string: " + stgSubmit);

		if (stgSubmit !== undefined) {
			app.log.info(this.name + " - submitting data to thermostat: " + stgSubmit);
			var rslt = exec(stgSubmit, function (error, stdout, stderr) {
				stdout.replace(/(\n|\r|\r\n)$/, '');
				app.log.info(this.name + " - Result: " + stdout);
				setTimeout( function() { updateDevices(app, opts) }, pauseAfterSetToUpdate);
			});
		}
		else {
			app.log.info(this.name + ' - error parsing data!');
		};
	}
	else {
		app.log.info("radioThermostatDriver Device " + this.name + " received data, but this type of device can't update");
	}
};

Driver.prototype.config = function(rpc,cb) {
	var self = this;
	if (!rpc) {
		this._app.log.info("radioThermostatDriver main config window called");
		return cb(null, {	// main config window
			"contents":[
				{ "type": "paragraph", "text": "The radioThermostatDriver allows you to monitor and control WiFi Thermostats such as the ones from radiothermostat.com. Enter the settings below to get started, and please make sure you get a confirmation message after hitting 'Submit' below. (You may have to click it a couple of times. If you don't get a confirmation message, the settings did not update!)"},
				{ "type": "input_field_text", "field_name": "ip_addr_text", "value": "", "label": "IP Address of Your Thermostat", "placeholder": "192.168.1.135", "required": true},
				{ "type": "input_field_text", "field_name": "update_int_secs_text", "value": "", "label": "Update Interval in Seconds", "placeholder": "600", "required": true},
				{ "type": "input_field_text", "field_name": "pause_bt_cmds_secs_text", "value": "", "label": "Seconds to Pause Between Thermostat Commands", "placeholder": "5", "required": true},
				{ "type": "input_field_text", "field_name": "pause_aft_updt_secs_text", "value": "", "label": "Seconds to Pause After a Command Before Updating", "placeholder": "10", "required": true},
				{ "type": "paragraph", "text": " "},
				{ "type": "submit", "name": "Submit", "rpc_method": "submt" },
				{ "type": "close", "name": "Cancel" },
			]
		});
	};
	if (rpc.method == "submt") {
		this._app.log.info("radioThermostatDriver config window submitted. Checking data for errors...");
		// check for errors
		var rgx = new RegExp("^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$"); // Test ip_addr_text to match this regex - simple ip address checking does not check nodes for > 255, but should suffice for now...
		if (!rgx.test(rpc.params.ip_addr_text)) {
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "IP address was invalid - please use standard ipv4 address, such as: 123.45.6.789. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else if (!(rpc.params.update_int_secs_text >= 0)) {	// update_int_secs_text must evaluate to a positive number
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "the 'update interval' must be a number and can't be negative. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else if (!(rpc.params.pause_bt_cmds_secs_text >= 0)) {	// pause_bt_cmds_secs_text must evaluate to a positive number
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "The 'pause between commands' interval must be a number and can't be negative. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else if (!(rpc.params.pause_aft_updt_secs_text >= 0)) {	// pause_aft_updt_secs_text must evaluate to a positive number
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "The 'pause after update' interval must be a number and can't be negative. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else {	// looks like the submitted values were valid, so update
			this._app.log.info("radioThermostatDriver data appears valid. Saving settings...");
            self.opts.ipadr = rpc.params.ip_addr_text;
            self.opts.updtInt = rpc.params.update_int_secs_text * 1000; // need this in milliseconds
			self.opts.pauseBtCmds = rpc.params.pause_bt_cmds_secs_text; // this optin isn't used right now...
			self.opts.pauseAftUpdt = rpc.params.pause_aft_updt_secs_text * 1000; // also need this in milliseconds
			self.save();
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "Configuration was successful. (TODO: actually update!) radioThermostatDriver values should update shortly!" },
					{ "type": "close"    , "name": "Close" }
				]
			});
			updateDevices(this._app, self.opts);
		};
	}
	else {
		this._app.log.info("radioThermostatDriver - Unknown rpc method was called!");
	};
};

module.exports = Driver;

<commit_msg>fix invalid config<commit_after>var util = require('util');
var stream = require('stream');
var exec = require('child_process').exec;
var commands = require('./commands');

util.inherits(Driver,stream);
util.inherits(Device,stream);

// not so elegant way to store all of the devices created by the driver, plus a few variables...
var deviceList = [];
var updateInterval = 300000; // update interval in milliseconds (for example, 600000 = 10 minutes)
var pauseAfterSetToUpdate = 7000; // time in milliseconds to wait after submitting/setting data before we try to run an update
var pauseBetweenUpdateCommands = 5; // time in seconds to wait between submitting/setting the two data requests - this is not currently used...
var ipAddressOfThermostat = "192.168.1.111"; // ip address of the thermostat

function Driver(opts,app) {
	this._app = app;
	this.opts = opts;
	if (opts.ipadr) ipAddressOfThermostat = opts.ipadr; // ugly way to track these, but it should work for now...
	if (opts.updtInt) updateInterval = opts.updtInt;
	if (opts.pauseAftUpdt) pauseAfterSetToUpdate = opts.pauseAftUpdt;
	app.once('client::up',function(){
		commands.forEach( this.createCommandDevice.bind(this) );
		updateDevices(app, opts);
		process.nextTick(function() {	// Once all devices are set up, establish a single update process that updates every "updateInterval" seconds
			setInterval(function() {
				updateDevices(app, opts);
			}, updateInterval);
		});
	}.bind(this));
};

Driver.prototype.createCommandDevice = function(cmd) {
	var d = new Device(this._app, cmd);
	this.emit('register', d);
	deviceList.push(d);
};

function Device(app, config) {
	app.log.info('Creating radioThermostatDriver Device : ' + config.name);
	var self = this;
	this._app = app;
	this.config = config;
	this.readable = true;
	this.writeable = config.canSet || false;
	if (this.writeable) { app.log.info('radioThermostatDriver Device ' + config.name + ' is readable and writable' ); } else app.log.info('radioThermostatDriver Device ' + config.name + ' is readable only' );
	this.V = config.vendorId || 0;
	this.D = config.deviceId;
	this.G = 'rtd' + (config.name).replace(/[^a-zA-Z0-9]/g, '');
	this.name = 'radioThermostatDriver - ' + config.name;
	// this.read();
};

function updateDevices(app, opts) {	// runs every "updateInterval" seconds
	app.log.info("Updating radioThermostatDriver Devices...");
	var tstatCmd = "curl -s http://" + ipAddressOfThermostat + "/tstat";
	app.log.info("radioThermostatDriver executing command: " + tstatCmd);
	exec(tstatCmd, function(error, stdout, stderr) {
		app.log.info("Result of radioThermostatDriver command: " + stdout);
		if (error) {
			if (error != null) {
				app.log.warn('radioThermostatDriver : ' + this.name + ' error! - ' + error);
			};
		}
		else if (stderr) {
			if (stderr != null) {
				app.log.warn('radioThermostatDriver : ' + this.name + ' stderr! - ' + stderr);
			};
		}
		else {
			var inputString = (stdout + '');
			var thermostatData = eval ("(" + inputString + ")");
			if (!thermostatData) {
				app.log.warn('radioThermostatDriver was unable to parse data recieved. No update was made this cycle.');
			}
			else {
				deviceList.forEach(function(dev){
					app.log.info('Updating radioThermostatDriver Device: ' + dev.name);
					var parsedResult = undefined;
					(dev.config.data || []).forEach(function(fn) {
						try {
							parsedResult = fn(thermostatData);
						} catch(e) {
							parsedResult = undefined;
						}
						app.log.info(dev.name + ' - parse data: ' + inputString + ' --> ' + parsedResult);
					});
					if (parsedResult !== undefined) {
						app.log.info(dev.name + ' - emmitting data: ' + parsedResult);
						dev.emit('data', parsedResult);
					}
					else {
						app.log.info(dev.name + ' - did not emmit data!');
					};
				});
			};
		};
	});
};

Device.prototype.write = function(dataRcvd) {
	var app = this._app;
	var opts = this.opts;
	app.log.info("radioThermostatDriver Device " + this.name + " received data: " + dataRcvd);
	app.log.info("radioThermostatDriver Device canSet: " + this.config.canSet);
	if (this.config.canSet) {
		var stgSubmit = undefined;
		(this.config.setStg || []).forEach(function(fn) {
			try {
				stgSubmit = fn(ipAddressOfThermostat, dataRcvd);
			} catch(e) {
				stgSubmit = undefined;
			}
		});
		app.log.info("radioThermostatDriver string: " + stgSubmit);
		if (stgSubmit !== undefined) {
			app.log.info(this.name + " - submitting data to thermostat: " + stgSubmit);
			var rslt = exec(stgSubmit, function (error, stdout, stderr) {
				stdout.replace(/(\n|\r|\r\n)$/, '');
				app.log.info(this.name + " - Result: " + stdout);
				setTimeout( function() { updateDevices(app, opts) }, pauseAfterSetToUpdate);
			});
		}
		else {
			app.log.info(this.name + ' - error parsing data!');
		};
	}
	else {
		app.log.info("radioThermostatDriver Device " + this.name + " received data, but this type of device can't update");
	}
};

Driver.prototype.config = function(rpc,cb) {
	var self = this;
	if (!rpc) {
		this._app.log.info("radioThermostatDriver main config window called");
		return cb(null, {	// main config window
			"contents":[
				{ "type": "paragraph", "text": "The radioThermostatDriver allows you to monitor and control WiFi Thermostats such as the ones from radiothermostat.com. Enter the settings below to get started, and please make sure you get a confirmation message after hitting 'Submit' below. (You may have to click it a couple of times. If you don't get a confirmation message, the settings did not update!)"},
				{ "type": "input_field_text", "field_name": "ip_addr_text", "value": "", "label": "IP Address of Your Thermostat", "placeholder": "192.168.1.135", "required": true},
				{ "type": "input_field_text", "field_name": "update_int_secs_text", "value": "", "label": "Update Interval in Seconds", "placeholder": "600", "required": true},
				{ "type": "input_field_text", "field_name": "pause_bt_cmds_secs_text", "value": "", "label": "Seconds to Pause Between Thermostat Commands", "placeholder": "5", "required": true},
				{ "type": "input_field_text", "field_name": "pause_aft_updt_secs_text", "value": "", "label": "Seconds to Pause After a Command Before Updating", "placeholder": "10", "required": true},
				{ "type": "paragraph", "text": " "},
				{ "type": "submit", "name": "Submit", "rpc_method": "submt" },
				{ "type": "close", "name": "Cancel" },
			]
		});
	};
	if (rpc.method == "submt") {
		this._app.log.info("radioThermostatDriver config window submitted. Checking data for errors...");
		// check for errors
		var rgx = new RegExp("^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$"); // Test ip_addr_text to match this regex - simple ip address checking does not check nodes for > 255, but should suffice for now...
		if (!rgx.test(rpc.params.ip_addr_text)) {
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "IP address was invalid - please use standard ipv4 address, such as: 123.45.6.789. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else if (!(rpc.params.update_int_secs_text >= 0)) {	// update_int_secs_text must evaluate to a positive number
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "the 'update interval' must be a number and can't be negative. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else if (!(rpc.params.pause_bt_cmds_secs_text >= 0)) {	// pause_bt_cmds_secs_text must evaluate to a positive number
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "The 'pause between commands' interval must be a number and can't be negative. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else if (!(rpc.params.pause_aft_updt_secs_text >= 0)) {	// pause_aft_updt_secs_text must evaluate to a positive number
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "The 'pause after update' interval must be a number and can't be negative. Please try again." },
					{ "type": "close"    , "name": "Close" }
				]
			});
			return;
		}
		else {	// looks like the submitted values were valid, so update
			this._app.log.info("radioThermostatDriver data appears valid. Saving settings...");
            self.opts.ipadr = rpc.params.ip_addr_text;
            self.opts.updtInt = rpc.params.update_int_secs_text * 1000; // need this in milliseconds
			self.opts.pauseBtCmds = rpc.params.pause_bt_cmds_secs_text; // this optin isn't used right now...
			self.opts.pauseAftUpdt = rpc.params.pause_aft_updt_secs_text * 1000; // also need this in milliseconds
			ipAddressOfThermostat = self.opts.ipadr; // ugly way to track these, but it should work for now...
			updateInterval = self.opts.ipadr;
			pauseAfterSetToUpdate = self.opts.ipadr;
			self.save();
			cb(null, {
				"contents": [
					{ "type": "paragraph", "text": "Configuration was successful. (TODO: actually update!) radioThermostatDriver values should update shortly!" },
					{ "type": "close"    , "name": "Close" }
				]
			});
			updateDevices(this._app, self.opts);
		};
	}
	else {
		this._app.log.info("radioThermostatDriver - Unknown rpc method was called!");
	};
};

module.exports = Driver;

<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var port = process.env.PORT || 3000;
var verificationToken = process.env.SLACK_VERIFICATION_TOKEN || 'foobar';
var clientID = process.env.SLACK_CLIENT_ID || 'foobar';
var clientSecret = process.env.SLACK_CLIENT_SECRET || 'foobar';
var callbackURL = process.env.SLACK_CALLBACK_URL || 'foobar';
var redisURL = process.env.REDIS_URL || 'foobar';
var appURL = process.env.APP_URL || 'foobar';
var bodyParser = require('body-parser');
var passport = require('passport');
var SlackStrategy = require('passport-slack').Strategy;
var redis = require('redis');
var client = redis.createClient(redisURL);
require('./lib.js')();

setInterval(function() {
  wakeUp(appURL);
}, 300000); // every 5 minutes (300000)

client.on('connect', function (err) {
  console.log('Redis client connected')
});

client.on('error', function(err){
  console.log('Redis encountered an error: '+err)
  console.log('we will now exit')
  process.exit(1);
})

app.use(bodyParser.urlencoded({extended: true}));

passport.use(new SlackStrategy({
    clientID: clientID,
    clientSecret: clientSecret,
    callbackURL: callbackURL,
    scope: 'commands chat:write:bot',
    skipUserProfile: true
    },
  function(accessToken, refreshToken, profile, done) {
    done(null,'foobar')
  }
));

app.get('/auth/slack', passport.authorize('slack'));

app.get('/auth/slack/callback', passport.authorize('slack', {successRedirect: 'https://my.slack.com', failureRedirect: '/login'}), function(req, res) {
    res.redirect('https://my.slack.com');
  });

//Slack token authentication middleware
app.get('/',function (req, res){
  res.json({"message":"OK"})
})

app.post('/secret/set', function (req, res) {
  var body = req.body
  if (body.token == verificationToken){
    res.end(null,function(err){ //send a 200 response
      sendSecret(body.response_url, body.user_name, body.text); //execute the action
      var redisKey = body.team_id + body.channel_id + body.user_id;
      client.set(redisKey, body.text)
    });
  } else {
    console.log('Failed token verification.');
    console.log('Expected token: '+verificationToken)
    console.log('Received token: '+req.body.token);
    return
  }
});

app.post('/secret/get', function (req, res) {
  var payload = safelyParseJson(req.body.payload);
  console.log(payload);
  if (payload && payload.token == verificationToken){
    var redisKey = payload.team.id + payload.channel.id + payload.user.id;
    client.get(redisKey, function(err,reply){
      var secret = ""
      if (err){
        console.log('error retrieving key from redis: '+err)
        return
      } else{
        secret = reply.toString();
        res.end(secret)
        updateMessage(payload, secret); //execute action
      }
    })
  } else {
    console.log('Null Payload or Failed token verification.');
    console.log('Expected token: '+verificationToken)
    console.log('Received token: '+req.body.token);
    return
  }
});
app.listen(process.env.PORT, function () {
  console.log('Example app listening');
  // console.log(safelyParseJson('{"payload":"{\"actions\":[{\"name\":\"readMessage\",\"value\":\"readMessage\"}],\"callback_id\":\"readMessage\",\"team\":{\"id\":\"T0BCJDZ8Q\",\"domain\":\"neufeldtech\"},\"channel\":{\"id\":\"C0BCEGD6X\",\"name\":\"general\"},\"user\":{\"id\":\"U0BCH4N2K\",\"name\":\"jordan.neufeld\"},\"action_ts\":\"1467468185.125008\",\"message_ts\":\"1467468175.000005\",\"attachment_id\":\"1\",\"token\":\"XiLexN3vvMIE5dXD8NlEM0Kn\",\"original_message\":{\"text\":\"\",\"bot_id\":\"B1NBWFTS6\",\"attachments\":[{\"callback_id\":\"readMessage\",\"fallback\":\"Please visit <http:\\/\\/secretmessage.neufeldtech.com>\",\"title\":\"jordan.neufeld sent a secret message:\",\"id\":1,\"color\":\"3AA3E3\",\"actions\":[{\"id\":\"1\",\"name\":\"readMessage\",\"text\":\"View message\",\"type\":\"button\",\"value\":\"readMessage\",\"style\":\"\",\"confirm\":{\"text\":\"yolo\",\"title\":\"This message will self destruct after reading!\",\"ok_text\":\"I have read the message!\",\"dismiss_text\":\"Cancel\"}}]}],\"type\":\"message\",\"subtype\":\"bot_message\",\"ts\":\"1467468175.000005\"},\"response_url\":\"https:\\/\\/hooks.slack.com\\/actions\\/T0BCJDZ8Q\\/56340699763\\/ZVVDA9AlAZbcekc8dWtH1JpE\"}"}'))
});
<commit_msg>commit first, test later<commit_after>var express = require('express');
var app = express();
var port = process.env.PORT || 3000;
var verificationToken = process.env.SLACK_VERIFICATION_TOKEN || 'foobar';
var clientID = process.env.SLACK_CLIENT_ID || 'foobar';
var clientSecret = process.env.SLACK_CLIENT_SECRET || 'foobar';
var callbackURL = process.env.SLACK_CALLBACK_URL || 'foobar';
var redisURL = process.env.REDIS_URL || 'foobar';
var appURL = process.env.APP_URL || 'foobar';
var bodyParser = require('body-parser');
var passport = require('passport');
var SlackStrategy = require('passport-slack').Strategy;
var redis = require('redis');
var client = redis.createClient(redisURL);
require('./lib.js')();

setInterval(function() {
  wakeUp(appURL);
}, 300000); // every 5 minutes (300000)

client.on('connect', function (err) {
  console.log('Redis client connected')
});

client.on('error', function(err){
  console.log('Redis encountered an error: '+err)
  console.log('we will now exit')
  process.exit(1);
})

app.use(bodyParser.urlencoded({extended: true}));

passport.use(new SlackStrategy({
    clientID: clientID,
    clientSecret: clientSecret,
    callbackURL: callbackURL,
    scope: 'commands chat:write:bot',
    skipUserProfile: true
    },
  function(accessToken, refreshToken, profile, done) {
    done(null,'foobar')
  }
));

app.get('/auth/slack', passport.authorize('slack'));

app.get('/auth/slack/callback', passport.authorize('slack', {successRedirect: 'https://my.slack.com', failureRedirect: '/login'}), function(req, res) {
    res.redirect('https://my.slack.com');
  });

//Slack token authentication middleware
app.get('/',function (req, res){
  res.json({"message":"OK"})
})

app.post('/secret/set', function (req, res) {
  var body = req.body
  if (body.token == verificationToken){
    res.end(null,function(err){ //send a 200 response
      sendSecret(body.response_url, body.user_name, body.text); //execute the action
      var redisKey = body.team_id + body.channel_id + body.user_id;
      client.set(redisKey, body.text)
    });
  } else {
    console.log('Failed token verification.');
    console.log('Expected token: '+verificationToken)
    console.log('Received token: '+req.body.token);
    return
  }
});

app.post('/secret/get', function (req, res) {
  var payload = safelyParseJson(req.body.payload);
  console.log(payload);
  if (payload && payload.token == verificationToken){
    var redisKey = payload.team.id + payload.channel.id + payload.user.id;
    client.get(redisKey, function(err,reply){
      var secret = ""
      if (err){
        console.log('error retrieving key from redis: '+err)
        return
      } else{
        secret = reply.toString();
        res.send(secret)
        //updateMessage(payload, secret); //execute action
      }
    });
    client.del(redisKey);
  } else {
    console.log('Null Payload or Failed token verification.');
    console.log('Expected token: '+verificationToken)
    console.log('Received token: '+req.body.token);
    return
  }
});
app.listen(process.env.PORT, function () {
  console.log('Example app listening');
  // console.log(safelyParseJson('{"payload":"{\"actions\":[{\"name\":\"readMessage\",\"value\":\"readMessage\"}],\"callback_id\":\"readMessage\",\"team\":{\"id\":\"T0BCJDZ8Q\",\"domain\":\"neufeldtech\"},\"channel\":{\"id\":\"C0BCEGD6X\",\"name\":\"general\"},\"user\":{\"id\":\"U0BCH4N2K\",\"name\":\"jordan.neufeld\"},\"action_ts\":\"1467468185.125008\",\"message_ts\":\"1467468175.000005\",\"attachment_id\":\"1\",\"token\":\"XiLexN3vvMIE5dXD8NlEM0Kn\",\"original_message\":{\"text\":\"\",\"bot_id\":\"B1NBWFTS6\",\"attachments\":[{\"callback_id\":\"readMessage\",\"fallback\":\"Please visit <http:\\/\\/secretmessage.neufeldtech.com>\",\"title\":\"jordan.neufeld sent a secret message:\",\"id\":1,\"color\":\"3AA3E3\",\"actions\":[{\"id\":\"1\",\"name\":\"readMessage\",\"text\":\"View message\",\"type\":\"button\",\"value\":\"readMessage\",\"style\":\"\",\"confirm\":{\"text\":\"yolo\",\"title\":\"This message will self destruct after reading!\",\"ok_text\":\"I have read the message!\",\"dismiss_text\":\"Cancel\"}}]}],\"type\":\"message\",\"subtype\":\"bot_message\",\"ts\":\"1467468175.000005\"},\"response_url\":\"https:\\/\\/hooks.slack.com\\/actions\\/T0BCJDZ8Q\\/56340699763\\/ZVVDA9AlAZbcekc8dWtH1JpE\"}"}'))
});
<|endoftext|>