<commit_before>var http = require("http"),
	https = require("https"),
	url = require("url");

module.exports = function(options, cb){
	if(typeof options === "string"){
		options = { uri: options };
	}
	return new Request(options, cb);
};

//Add methods for the most common cases
["GET", "POST", "HEAD", "DELETE", "UPDATE"].forEach(function(method){
	module.exports[method.toLowerCase()] = function(uri, cb){
		uri = url.parse(uri);
		uri.method = method;
		return new Request({ uri: uri }, cb);
	};
});

var protocols = {
	"http:": http,
	"https:": https
};
module.exports.addProtocol = function(name, module){
	protocols[name] = module;
};

/*
* Options:
*	uri: Object that's passed to http(s).request (http://nodejs.org/docs/latest/api/all.html#http.request)
*	followRedirect: Boolean that indicates whether redirects should be followed
*	maxRedirects: int with the maximum number of redirects (defaults to 10)
*	body: that data that should be passed to the request
*	encoding: the encoding that all data should use (the body will always be a string)
*	timeout: a request times out if it passes this limit. Defaults to 10000 (read: 10 seconds)
*/

var Request = function(options, cb){
	this._options = options;
	this._ended = false;
	this._resp = null;
	this.response = null;

	this._cb = cb;
	this._body = "";

	this._createRequest(options);
	if(!this._request) return;

	var scope = this;

	this.on("error", function(err){
		scope._cb(err);
	}).on("end", function(){
		scope._cb(null, scope._headers, scope._body);
	}).on("data", function(chunk){
		if(!scope._written) scope._written = true;
		scope._body += chunk;
	});

	if(options.body) this._request.write(options.body);

	this.writable = options.uri.method === "POST" || options.uri.method === "PUT";
	this._prepareClose();

	if(!this.writable){
		this._request.end();
	}
	else{
		if(options.body){
			this._request.write(options.body);
		}
		this.once("pipe", function(src){
			if(!scope.writable){
				scope.emit("error", Error("Can't write to socket!"));
				return;
			}
			scope._close = false;
			var cb = function(){
				scope._prepareClose();
			};
			src.on("end", cb);
			src.on("close", cb);

			scope.on("pipe", function(){
				throw Error("There is already a pipe");
			});
		});
	}
};

var Stream = require("stream").Stream;
require("util").inherits(Request, Stream);

//save the pipe function for later
var pipe = Stream.prototype.pipe;

Request.prototype.pipe = function(dest, opts){
	if(this._written){
		throw Error("Data was already emitted!");
	}
	else if(this._ended){
		throw Error("Request is closed!");
	}
	else pipe.call(this, dest, opts);
};

Request.prototype._prepareClose = function(){
	this._close = true;
	var scope = this;
	process.nextTick(function(){
		if(scope._close){
			scope._request.end();
			scope.writable = false;
		}
	});
};

var re_protocol = /^https?:/; //TODO: what about other protocols?
var moveEvents = function(from, to){
	for(var i in from._events){
		if(typeof from._events[i] === "function"){
			to.on(i, from._events[i]);
		}
		else for(var j = 0; j < from._events[i].length; j++){
			to.on(i, from._events[i][j]);
		}
	}
	from._events = to._events;
};

Request.prototype._createRequest = function(options){
	if(typeof options.uri === "string"){
		options.uri = url.parse(options.uri);
	}
	else if(typeof options.uri !== "object"){
		scope.emit("error", Error("No URI specified!"));
		return;
	}

	//fix for node < 0.5
	if(!options.uri.path) options.uri.path = options.uri.pathname;

	var req = protocols[options.uri.protocol];
	if(!req) return scope.emit("error", Error("Unknown protocol: " + options.uri.protocol));

	var scope = this;

	this._request = req.request(options.uri, function(resp){
		var method = options.uri.method;

		if( (options.followRedirects || typeof options.followRedirect === "undefined") && (resp.statusCode % 300 < 99) && !scope.writable && resp.headers.location){
				if(!scope._redirected) scope._redirected = 0;

				scope._request.abort(); //close the socket
				clearTimeout(scope._reqTimeout);

				scope.emit("redirect", resp.headers.location);

				if(scope._redirected++ < (options.maxRedirects || 10)){
					if(!re_protocol.test(resp.headers.location)){
						resp.headers.location = url.resolve(options.uri, resp.headers.location);
					}

					options.uri = url.parse(resp.headers.location);
					options.uri.method = method;

					scope._createRequest(options);
				} else {
					scope.emit("error", Error("Too many redirects"));
				}
				return;
		}

		//add some info to the scope
		scope.response = {
			location: url.format(options.uri),
			statusCode: response.statusCode,
			headers: resp.headers
		};

		scope._resp = resp;

		if(options.encoding){
			resp.setEncoding(options.encoding);
		}

		resp.on("data", function(chunk){
			scope.emit("data", chunk);
		}).on("end", function(){
			if(!scope._ended) scope.emit("end");
		}).on("close", function(){
			scope.emit("close");
		}).on("error", function(err){
			scope.emit("error", err);
		});

		scope.emit("response", resp);
	});

	if(!("timeout" in this._options) || this._options.timeout){
		this._reqTimeout = setTimeout(function(){
			if(!scope._ended){
				scope._request.abort();

				scope.emit("timeout");
				scope.emit("error", Error("ETIMEDOUT"));
			}
		}, this._options.timeout || 1e4);
	}
};

Request.prototype.readable = true;

Request.prototype.setEncoding = function(encoding){
	//if we are connected, send the encoding to the response
	if(this._resp) this._resp.setEncoding(encoding);
	//else, safe it for later
	else this._options.encoding = encoding;
};
Request.prototype.then = function(cb){
	//for promise-like behavior
	this._cb = cb;
};
Request.prototype.abort = function(){
	this._request.abort();
};
Request.prototype.write = function(chunk){
	if(!this.writable) throw Error("Ether request method doesn't support .write or request was sent!");
	return this._request.write(chunk);
};

module.exports.Request = Request;<commit_msg>Restructured code, changed many things<commit_after>var http = require("http"),
	https = require("https"),
	url = require("url");

module.exports = function(options, cb){
	if(typeof options === "string"){
		options = { uri: url.parse(options) };
	}
	return new Request(options, cb);
};

//Add methods for the most common cases
["GET", "POST", "HEAD", "DELETE", "UPDATE"].forEach(function(method){
	module.exports[method.toLowerCase()] = function(uri, cb){
		uri = url.parse(uri);
		uri.method = method;
		return new Request({ uri: uri }, cb);
	};
});

var protocols = {
	"http:": http,
	"https:": https
};
module.exports.addProtocol = function(name, module){
	protocols[name] = module;
};

/*
* Options:
*	uri: Object that's passed to http(s).request (http://nodejs.org/docs/latest/api/all.html#http.request)
*	followRedirect: Boolean that indicates whether redirects should be followed
*	maxRedirects: int with the maximum number of redirects (defaults to 10)
*	body: that data that should be passed to the request
*	encoding: the encoding that all data should use (the body will always be a string)
*	timeout: a request times out if it passes this limit. Defaults to 10000 (read: 10 seconds)
*/

var re_protocol = /^https?:/; //TODO: what about other protocols?

var Request = function(options, cb){
	this._options = options;
	this._cb = cb;

	this._ended = false;
	this._redirected = 0;
	this._resp = null;
	this.response = null;
	this._body = "";

	this.writable = options.uri.method === "POST" || options.uri.method === "PUT";

	this._addListeners();

	var scope = this;
	process.nextTick(function(){
		if(!scope._createRequest(options)) return;
		if(!scope.writable) scope._request.end();
		else{
			if(options.body) scope._request.write(options.body);
			scope._prepareClose();
		}
	});
};

var Stream = require("stream").Stream;
require("util").inherits(Request, Stream);

Request.prototype.readable = true;

//save the pipe function for later
var pipe = Stream.prototype.pipe;

Request.prototype.pipe = function(dest, opts){
	if(this._body){
		throw Error("Data was already emitted!");
	}
	else if(this._ended){
		throw Error("Request is closed!");
	}
	else pipe.call(this, dest, opts);
};

Request.prototype._prepareClose = function(){
	this._close = true;
	var scope = this;
	process.nextTick(function(){
		if(scope._close){
			scope._request.end();
			scope.writable = false;
		}
	});
};

Request.prototype._addListeners = function(){
	var scope = this;

	this.on("error", function(err){
		scope._cb(err);
	});

	this.on("end", function(){
		scope._ended = true;
		scope._cb(null, scope.response, scope._body);
	});

	this.on("data", function(chunk){
		if(!scope._written) scope._written = true;
		scope._body += chunk;
	});

	this.on("redirect", function(location){
		var options = scope._options,
			method = options.uri.method;

		if(scope._redirected++ < (options.maxRedirects || 10)){
			if(!re_protocol.test(location)){
	    		location = url.resolve(options.uri, location);
	    	}

	    	options.uri = url.parse(location);
	    	options.uri.method = method;

	    	scope._createRequest(options);
	    	scope._request.end();
	    } else {
	    	scope.emit("error", Error("Too many redirects"));
	    }
	});

	this.once("pipe", function(src){
		if(!scope.writable){
		 	scope.emit("error", Error("Can't write to socket!"));
		 	return;
		 }

		 scope._close = false;
		 var cb = function(){
		 	scope._prepareClose();
		 };
		 src.on("end", cb);
		 src.on("close", cb);

		 scope.on("pipe", function(){
		 	throw Error("There is already a pipe");
		 });
	});
};

Request.prototype._createRequest = function(options){
	if(typeof options.uri !== "object"){
		this.emit("error", Error("No URI specified!"));
		return;
	}

	//fix for node < 0.5
	if(!options.uri.path) options.uri.path = options.uri.pathname;

	var req = protocols[options.uri.protocol];
	if(!req) return this.emit("error", Error("Unknown protocol: " + options.uri.protocol));

	var scope = this;

	this._request = req.request(options.uri, function(resp){
		if( (options.followRedirects || typeof options.followRedirect === "undefined") && (resp.statusCode % 300 < 99) && !scope.writable && resp.headers.location){
				clearTimeout(scope._reqTimeout);
				scope._request.abort(); //close the socket
				scope.emit("redirect", resp.headers.location);
				return;
		}

		//add some info to the scope
		scope.response = {
			location: url.format(options.uri),
			statusCode: resp.statusCode,
			headers: resp.headers
		};

		scope._resp = resp;

		if(options.encoding){
			resp.setEncoding(options.encoding);
		}

		resp.on("end", function(){
			if(!scope._ended) scope.emit("end");
		}).on("close", function(){
			scope.emit("close");
		}).on("error", function(err){
			scope.emit("error", err);
		}).on("data", function(chunk){
			scope.emit("data", chunk);
		});

		scope.emit("response", resp);
	});

	if(!("timeout" in options) || options.timeout){
		this._reqTimeout = setTimeout(function(){
			if(!scope._ended){
				scope._request.abort();

				scope.emit("timeout");
				scope.emit("error", Error("ETIMEDOUT"));
			}
		}, options.timeout || 1e4);
	}

	return true;
};

Request.prototype.setEncoding = function(encoding){
	//if we are connected, send the encoding to the response
	if(this._resp) this._resp.setEncoding(encoding);
	//else, safe it for later
	else this._options.encoding = encoding;
};
Request.prototype.then = function(cb){
	//for promise-like behavior
	this._cb = cb;
};
Request.prototype.abort = function(){
	this._request.abort();
};
Request.prototype.write = function(chunk){
	if(!this.writable) throw Error("Either request method doesn't support .write or request was sent!");
	return this._request.write(chunk);
};

module.exports.Request = Request;<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
var md5 = require('MD5');
var db = require('./data.json');

app.get('/', function (req, res) {
    res.send('Hello World!')
});

function findObjectById(collection, id) {
    return db[collection].filter(function (auction) {
        return auction.id == id;
    })[0];
}

app.get('/auction/:id', function (req, res) {
    var auction = findObjectById('auction', req.params.id);
    auction ? res.send(auction) : res.sendStatus(404);
});

app.get('/friends', function (req, res) {
    var friends = db.friends.map(function (friend) {
        friend.imageUrl = 'http://www.gravatar.com/avatar/' + md5(friend.email) + '.jpg?s=500';
        return friend;
    });
    res.send(friends);
});

app.post('/beg', function (req, res) {
    ['auctionId', 'friendsId'].map(function (key) {
        if (!req.body.hasOwnProperty('auctionId')) {
            res.status(400);
            res.send('Missing key: ' + key);
        }
    });

    var auction = findObjectById('auctions', req.body.auctionId);
    if (!auction) {
        res.status(404);
        res.send('No auction for id: ' + req.body.auctionId);
        return;
    }

    req.body.friendsId.map(function (friendId) {
        var friend = findObjectById('friends', friendId);
        if (!friend) {
            res.status(404);
            res.send('No friend for id: ' + friendId);
        }
    });

    req.body.friendsId.map(function (friendId) {
        for(var i = 0; i < db.begs.length; i++) {
            var beg = db.begs[i];
            if (beg.auctionId == req.body.auctionId && beg.friendId == friendId) {
                return;
            }
        }
        db.begs.push({
            id: db.begs.length,
            auctionId: req.body.auctionId,
            friendId: friendId
        });
    });

    var begs = db.begs.filter(function(beg){
        return beg.auctionId == req.body.auctionId;
    });

    res.send(begs);
});

var server = app.listen(8080, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Example app listening at http://%s:%s', host, port)
});

<commit_msg>Fixed heroku port.<commit_after>var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
var md5 = require('MD5');
var db = require('./data.json');

app.get('/', function (req, res) {
    res.send('Hello World!')
});

function findObjectById(collection, id) {
    return db[collection].filter(function (auction) {
        return auction.id == id;
    })[0];
}

app.get('/auction/:id', function (req, res) {
    var auction = findObjectById('auction', req.params.id);
    auction ? res.send(auction) : res.sendStatus(404);
});

app.get('/friends', function (req, res) {
    var friends = db.friends.map(function (friend) {
        friend.imageUrl = 'http://www.gravatar.com/avatar/' + md5(friend.email) + '.jpg?s=500';
        return friend;
    });
    res.send(friends);
});

app.post('/beg', function (req, res) {
    ['auctionId', 'friendsId'].map(function (key) {
        if (!req.body.hasOwnProperty('auctionId')) {
            res.status(400);
            res.send('Missing key: ' + key);
        }
    });

    var auction = findObjectById('auctions', req.body.auctionId);
    if (!auction) {
        res.status(404);
        res.send('No auction for id: ' + req.body.auctionId);
        return;
    }

    req.body.friendsId.map(function (friendId) {
        var friend = findObjectById('friends', friendId);
        if (!friend) {
            res.status(404);
            res.send('No friend for id: ' + friendId);
        }
    });

    req.body.friendsId.map(function (friendId) {
        for(var i = 0; i < db.begs.length; i++) {
            var beg = db.begs[i];
            if (beg.auctionId == req.body.auctionId && beg.friendId == friendId) {
                return;
            }
        }
        db.begs.push({
            id: db.begs.length,
            auctionId: req.body.auctionId,
            friendId: friendId
        });
    });

    var begs = db.begs.filter(function(beg){
        return beg.auctionId == req.body.auctionId;
    });

    res.send(begs);
});

var server = app.listen(process.env.PORT || 8080, function () {
    var host = server.address().address;
    var port = server.address().port;
    console.log('Example app listening at http://%s:%s', host, port)
});

<|endoftext|>


<commit_before>'use strict';

/**
 * Library that makes it easier to invoke lambda functions with promises.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  29 Jul. 2015
 */

// module dependencies
var AWS = require('aws-sdk'),
    Q = require('q');

// Create the lambda function
var lambda = new AWS.Lambda();

module.exports = {
    /**
     * Invokes another lambda function asynchronously. This means that the InvocationType is set
     * to `Event` and it will return after the invocation succeeded.
     *
     * @param  {string}     name        The name of the lambda function to invoke.
     * @param  {object}     payload     The payload that should be send to the lambda function.
     * @return {Promise}                The promise object.
     */
    invokeAsync: function(name, payload) {
        return Q.promise(function(resolve, reject) {
            if(!name) {
                // If the function is undefined, just resolve
                throw new Error('Please provide a name')
            }

            // Build up the message params
            var params = {
                FunctionName: name,
                InvocationType: 'Event',
                Payload: JSON.stringify(payload)
            };

            // Invoke another lambda function
            lambda.invoke(params, function(err, data) {
                if(err) {
                    // Reject if something went wrong
                    return reject(err);
                }

                // Resolve if everything went well
                resolve();
            })
        });
    }
};<commit_msg>Added invoke method<commit_after>'use strict';

/**
 * Library that makes it easier to invoke lambda functions with promises.
 *
 * @author Sam Verschueren      <sam.verschueren@gmail.com>
 * @since  29 Jul. 2015
 */

// module dependencies
var AWS = require('aws-sdk'),
    Q = require('q');

// Create the lambda function
var lambda = new AWS.Lambda();

module.exports = {
    /**
     * Invokes another lambda function asynchronously. This means that the InvocationType is set
     * to `Event` and it will return after the invocation succeeded.
     *
     * @param  {string}     name        The name of the lambda function to invoke.
     * @param  {object}     payload     The payload that should be send to the lambda function.
     * @return {Promise}                The promise object.
     */
    invokeAsync: function(name, payload) {
        return Q.promise(function(resolve, reject) {
            if(!name) {
                // If the function is undefined, just resolve
                throw new Error('Please provide a name')
            }

            // Build up the message params
            var params = {
                FunctionName: name,
                InvocationType: 'Event',
                Payload: JSON.stringify(payload)
            };

            // Invoke another lambda function
            lambda.invoke(params, function(err, data) {
                if(err) {
                    // Reject if something went wrong
                    return reject(err);
                }

                // Resolve if everything went well
                resolve();
            })
        });
    },
    /**
     * Invokes another lambda function synchronously. This means that the InvocationType is set
     * to `RequestResponse` and it will return after the called lambda function returns the result.
     *
     * @param  {string}     name        The name of the lambda function to invoke.
     * @param  {object}     payload     The payload that should be send to the lambda function.
     * @return {Promise}                The promise object.
     */
    invoke: function(name, payload) {
        return Q.promise(function(resolve, reject) {
            if(!name) {
                // If the function is undefined, just resolve
                throw new Error('Please provide a name')
            }

            // Build up the message params
            var params = {
                FunctionName: name,
                InvocationType: 'RequestResponse',
                Payload: JSON.stringify(payload)
            };

            // Invoke another lambda function
            lambda.invoke(params, function(err, data) {
                if(err) {
                    // Reject if something went wrong
                    return reject(err);
                }

                // Resolve if everything went well
                resolve();
            })
        });
    }
};<|endoftext|>


<commit_before>'use strict';

// Exports a train singleton
var db = require('./backend/db.js');
var Q = require('q');
var extend = require('extend');
var fs = require('fs');
var log = require('../../lib/logger');
var nodecg = {};

function Train(extensionApi) {
    if ( Train.prototype._singletonInstance ) {
        return Train.prototype._singletonInstance;
    }
    Train.prototype._singletonInstance = this;

    nodecg = extensionApi;
    var self = this;

    // These properties are transient and are not persisted in the DB
    nodecg.declareSyncedVar({ variableName: 'elapsedTime', initialVal: 0 });
    nodecg.declareSyncedVar({ variableName: 'remainingTime', initialVal: 0 });
    nodecg.declareSyncedVar({ variableName: 'isCooldownActive', initialVal: false });

    this._timer = null;

    // Set up options
    this.options = {};
    this.initializeOptions();

    this.init()
        .then(function(train) {
            nodecg.declareSyncedVar({ variableName: 'passengers',
                initialVal: train.passengers,
                setter: function(newVal) {
                    self.write({passengers: newVal});
                }
            });
            nodecg.declareSyncedVar({ variableName: 'dayTotal',
                initialVal: train.dayTotal,
                setter: function(newVal) {
                    self.write({dayTotal: newVal});
                }
            });
            nodecg.declareSyncedVar({ variableName: 'threshold',
                initialVal: train.threshold,
                setter: function(newVal) {
                    self.write({threshold: newVal});
                }
            });
            nodecg.declareSyncedVar({ variableName: 'duration',
                initialVal: train.duration,
                setter: function(newVal) {
                    self.write({duration: newVal});
                }
            });
        })
        .fail(function(err) {
            throw err;
        });

    nodecg.listenFor('startCooldown', this.startCooldown.bind(this));
    nodecg.listenFor('endCooldown', this.endCooldown.bind(this));
    nodecg.listenFor('resetCooldown', this.resetCooldown.bind(this));

    // temporary workaround for some bundles until I figure out how to make this better
    nodecg.listenFor('getPassengers', function(data, cb) {
        cb(nodecg.variables.passengers);
    });
    nodecg.listenFor('getDayTotal', function(data, cb) {
        cb(nodecg.variables.dayTotal);
    });
}

Train.prototype.initializeOptions = function() {
    var cfgPath = __dirname + '/config.json';
    var config = {};
    if (fs.existsSync(cfgPath)) {
        config = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
    }

    // Intialize options to default values
    this.options.autoStartCooldown = false; // If 'true', automatically starts the cooldown when a passenger is added
    this.options.resetAfterThreshold = false; // If 'true', hitting the threshold causes the train to reset to zero

    // Overwrite defaults with any options from the config
    extend (true, this.options, config);
};

Train.prototype.init = function() {
    var deferred = Q.defer();
    db.findOne({ _id: 'train' }, function (err, doc) {
        if (err) {
            deferred.reject(new Error(err));
        } else if (doc === null) {
            // If the train isn't in the DB, make a new one with defaults
            var defaultTrain = {
                _id: 'train',
                passengers: 0,
                dayTotal: 0,
                threshold: 10,
                duration: 300
            };

            db.insert(defaultTrain, function (err, newDoc) {
                if (err) {
                    deferred.reject(new Error(err));
                } else {
                    deferred.resolve(newDoc);
                }
            });
        } else {
            // Else, return the train already present in the DB
            deferred.resolve(doc);
        }
    });
    return deferred.promise;
};

// Allows for setting any or all of the properties.
Train.prototype.write = function(args) {
    db.update({ _id: 'train' }, { $set: args }, { upsert: true }, function (err, numAdded) {
        if (err)
            log.error(err.stack);
    });
};

Train.prototype.startCooldown = function () {
    this._killTimer(); // Kill any existing cooldown timer

    nodecg.variables.elapsedTime = 0;
    nodecg.variables.remainingTime = nodecg.variables.duration;
    nodecg.variables.isCooldownActive = true;
    this._timer = setInterval(this.tickCooldown.bind(this), 1000);

    nodecg.sendMessage('cooldownStart');
};

Train.prototype.tickCooldown = function() {
    nodecg.variables.elapsedTime++;
    nodecg.variables.remainingTime--;

    if (nodecg.variables.remainingTime <= 0) {
        nodecg.variables.remainingTime = 0; // force to zero if we somehow went negative
        this.endCooldown();
    }
};

Train.prototype.resetCooldown = function() {
    nodecg.variables.elapsedTime = 0;
    nodecg.variables.remainingTime = nodecg.variables.duration;
};

Train.prototype.endCooldown = function() {
    this._killTimer();
    this.write({ passengers: 0 });
    nodecg.variables.isCooldownActive = false;
    nodecg.sendMessage('cooldownEnd');
};

Train.prototype._killTimer = function () {
    if (this._timer !== null) {
        clearInterval(this._timer);
        this._timer = null;
    }
};

Train.prototype.addPassenger = function() {
    nodecg.variables.passengers++;
    nodecg.variables.dayTotal++;

    if (this.options.autoStartCooldown)
        this.startCooldown();

    return nodecg.variables;
};

module.exports = function(extensionApi) { return new Train(extensionApi) };<commit_msg>fix passengers not being reset to zero at end of coutndown<commit_after>'use strict';

// Exports a train singleton
var db = require('./backend/db.js');
var Q = require('q');
var extend = require('extend');
var fs = require('fs');
var log = require('../../lib/logger');
var nodecg = {};

function Train(extensionApi) {
    if ( Train.prototype._singletonInstance ) {
        return Train.prototype._singletonInstance;
    }
    Train.prototype._singletonInstance = this;

    nodecg = extensionApi;
    var self = this;

    // These properties are transient and are not persisted in the DB
    nodecg.declareSyncedVar({ variableName: 'elapsedTime', initialVal: 0 });
    nodecg.declareSyncedVar({ variableName: 'remainingTime', initialVal: 0 });
    nodecg.declareSyncedVar({ variableName: 'isCooldownActive', initialVal: false });

    this._timer = null;

    // Set up options
    this.options = {};
    this.initializeOptions();

    this.init()
        .then(function(train) {
            nodecg.declareSyncedVar({ variableName: 'passengers',
                initialVal: train.passengers,
                setter: function(newVal) {
                    self.write({passengers: newVal});
                }
            });
            nodecg.declareSyncedVar({ variableName: 'dayTotal',
                initialVal: train.dayTotal,
                setter: function(newVal) {
                    self.write({dayTotal: newVal});
                }
            });
            nodecg.declareSyncedVar({ variableName: 'threshold',
                initialVal: train.threshold,
                setter: function(newVal) {
                    self.write({threshold: newVal});
                }
            });
            nodecg.declareSyncedVar({ variableName: 'duration',
                initialVal: train.duration,
                setter: function(newVal) {
                    self.write({duration: newVal});
                }
            });
        })
        .fail(function(err) {
            throw err;
        });

    nodecg.listenFor('startCooldown', this.startCooldown.bind(this));
    nodecg.listenFor('endCooldown', this.endCooldown.bind(this));
    nodecg.listenFor('resetCooldown', this.resetCooldown.bind(this));

    // temporary workaround for some bundles until I figure out how to make this better
    nodecg.listenFor('getPassengers', function(data, cb) {
        cb(nodecg.variables.passengers);
    });
    nodecg.listenFor('getDayTotal', function(data, cb) {
        cb(nodecg.variables.dayTotal);
    });
}

Train.prototype.initializeOptions = function() {
    var cfgPath = __dirname + '/config.json';
    var config = {};
    if (fs.existsSync(cfgPath)) {
        config = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
    }

    // Intialize options to default values
    this.options.autoStartCooldown = false; // If 'true', automatically starts the cooldown when a passenger is added
    this.options.resetAfterThreshold = false; // If 'true', hitting the threshold causes the train to reset to zero

    // Overwrite defaults with any options from the config
    extend (true, this.options, config);
};

Train.prototype.init = function() {
    var deferred = Q.defer();
    db.findOne({ _id: 'train' }, function (err, doc) {
        if (err) {
            deferred.reject(new Error(err));
        } else if (doc === null) {
            // If the train isn't in the DB, make a new one with defaults
            var defaultTrain = {
                _id: 'train',
                passengers: 0,
                dayTotal: 0,
                threshold: 10,
                duration: 300
            };

            db.insert(defaultTrain, function (err, newDoc) {
                if (err) {
                    deferred.reject(new Error(err));
                } else {
                    deferred.resolve(newDoc);
                }
            });
        } else {
            // Else, return the train already present in the DB
            deferred.resolve(doc);
        }
    });
    return deferred.promise;
};

// Allows for setting any or all of the properties.
Train.prototype.write = function(args) {
    db.update({ _id: 'train' }, { $set: args }, { upsert: true }, function (err, numAdded) {
        if (err)
            log.error(err.stack);
    });
};

Train.prototype.startCooldown = function () {
    this._killTimer(); // Kill any existing cooldown timer

    nodecg.variables.elapsedTime = 0;
    nodecg.variables.remainingTime = nodecg.variables.duration;
    nodecg.variables.isCooldownActive = true;
    this._timer = setInterval(this.tickCooldown.bind(this), 1000);

    nodecg.sendMessage('cooldownStart');
};

Train.prototype.tickCooldown = function() {
    nodecg.variables.elapsedTime++;
    nodecg.variables.remainingTime--;

    if (nodecg.variables.remainingTime <= 0) {
        nodecg.variables.remainingTime = 0; // force to zero if we somehow went negative
        this.endCooldown();
    }
};

Train.prototype.resetCooldown = function() {
    nodecg.variables.elapsedTime = 0;
    nodecg.variables.remainingTime = nodecg.variables.duration;
};

Train.prototype.endCooldown = function() {
    this._killTimer();
    this.write({ passengers: 0 });
    nodecg.variables.passengers = 0;
    nodecg.variables.isCooldownActive = false;
    nodecg.sendMessage('cooldownEnd');
};

Train.prototype._killTimer = function () {
    if (this._timer !== null) {
        clearInterval(this._timer);
        this._timer = null;
    }
};

Train.prototype.addPassenger = function() {
    nodecg.variables.passengers++;
    nodecg.variables.dayTotal++;

    if (this.options.autoStartCooldown)
        this.startCooldown();

    return nodecg.variables;
};

module.exports = function(extensionApi) { return new Train(extensionApi) };<|endoftext|>


<commit_before>var config = require('./config.js');
var flock = require('flockos');
var express = require('express');
var store = require('./store.js');
var chrono = require('chrono-node');
var Mustache = require('mustache');
var fs = require('fs');
var util = require('util');
var request = require('request');
var Client = require('node-rest-client').Client;
var client = new Client();

var watcher = require('./watcher.js');

flock.appId = config.appId;
flock.appSecret = config.appSecret;

var url = 'https://api.stackexchange.com/2.2/';
var searchUrlAppend = 'search?order=desc&sort=votes&site=stackoverflow&intitle=';

var questionAnswerUrlAppend = '?site=stackoverflow&filter=withbody';

// Change this when you deploy locally
var baseUrl = 'http://3676f534.ngrok.io/';

var app = express();
app.use(flock.events.tokenVerifier);
app.post('/events', flock.events.listener);

app.set('view engine', 'ejs');

app.listen(8080, function() {
    console.log('Listening on 8080');
});

// Start the watcher function async
setTimeout(function(){
    watcher.startWatcher(function(userId, questionId) {
        flock.chat.sendMessage(config.botToken, {
            to: userId,
            text: "Question Id " + questionId + " has changed!"
        });
    });
}, 0);

app.get('/addToWatchList/:userId/:questionId',function(req,res){

    var userId = req.params.userId;
    var questionId = req.params.questionId;

    console.log("userId = " + userId + " questionId " + questionId );
    watcher.addWatcher(userId, questionId);

    var date = new Date().toISOString().slice(0, 19).replace('T', ' ');

    res.send("Hi, we will be watching the question and notify you if any answers are added");
});

// TODO: Unimplemented method
app.get('/upvoteAnswer/:userId/:answerId', function(req, res){
    var userId = req.params.userId;
    var questionId = req.params.answerId;

    var date = new Date().toISOString().slice(0, 19).replace('T', ' ');

    res.send('Thank you for upvoting! :)');
});

// Return a list of question titles that the user is watching
app.get('/list',function(req,res){

    var event = JSON.parse(req.query.flockEvent);

    res.set('Content-Type', 'text/html');

    var userId = event.userId;

    console.log( "userId = " + userId);
    var watchList = watcher.getWatchList(userId);

    var questionTitles = getQuestionTitles(watchList[userId], res);
});

// Given a list of question id's, return a list of the titles of those questions
function getQuestionTitles(watchList, res) {
    var questionTitles = [];

    var questionUrl = url + '/questions/';
    console.log(watchList);

    for(var i = 0; i < watchList.length-1; i++) {
        console.log(i);
        var questionId = watchList[i];
        questionUrl = questionUrl + questionId + ';' ;
    }

    questionUrl = questionUrl + watchList[watchList.length - 1]+questionAnswerUrlAppend;
    console.log(questionUrl);
    client.get(questionUrl, function (data, response) {
        console.log("Got question title for watch list!");
        console.log(data);

        questionTitles = data.items.map(function(q) {return q.title})

        console.log("questionTitles "  + questionTitles);
        var body = '

'
        var end = '

'
        for (i in questionTitles) {
            body += '
'+ questionTitles[i] + "
";
        }
        body += end;
        console.log(body);
        res.send(body);
    });

    return questionTitles;
}

function sendMessageByBot(event, questions) {

    var css = ".btn { line-height: 30px;display: inline-block;float:left;width: 200px;text-align: center; cursor:pointer; background: #0abe50; background-image: -webkit-linear-gradient(top, #0abe50, #0abe50); background-image: -moz-linear-gradient(top, #0abe50, #0abe50); background-image: -ms-linear-gradient(top, #0abe50, #0abe50); background-image: -o-linear-gradient(top, #0abe50, #0abe50); background-image: linear-gradient(to bottom, #0abe50, #0abe50); -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0px; font-family: Arial; color: #f2f2f2; font-size: 14px; padding: 5px 5px 5px 5px; text-decoration: none; }.btn:hover { background: #5ce08f; background-image: -webkit-linear-gradient(top, #5ce08f, #5ce08f); background-image: -moz-linear-gradient(top, #5ce08f, #5ce08f); background-image: -ms-linear-gradient(top, #5ce08f, #5ce08f); background-image: -o-linear-gradient(top, #5ce08f, #5ce08f); background-image: linear-gradient(to bottom, #5ce08f, #5ce08f); text-decoration: none; }" ;
    var start = '<!DOCTYPE html><title>Questions</title>' ;
    var end = '';

    console.log("Firing BOT! :D");

    questions.forEach(function(e){

        var linkToWatch = baseUrl + 'addToWatchList/' + event.userId + '/'+ e.questionId ;

        e.answers.forEach(function(d){
            var linkToUpvote = baseUrl + 'upvoteAnswer/' + event.userId + '/' + d.answerId ;

            var start = '<!DOCTYPE html><title>Questions</title>';
            start += '
' + e.question + '
Watch

' ;
            start += '
' + d.answer + '
' ;
            start += '

Upvote this answer' + end;
            body = start;
            console.log(body);
            flock.chat.sendMessage(config.botToken, {
                to: event.userId,
                attachments: [{
                            "title": "We found something relevant on stackoverflow",
                            "views": {
                                "html": { "inline": body, "width" : 400, "height" : 400}
                            }
                    }]
            });
        });
    });
}

flock.events.on('app.install', function (event, callback) {

    flock.chat.sendMessage(config.botToken, {
            to: event.userId,
            text: "Welcome to the FlockSo app! FlockSo is StackOverflow for FlockOs"
    });

    store.saveToken(event.userId, event.token);
    callback();
});

flock.events.on('client.slashCommand', function (event, callback) {

    var commandText = event.text;
    console.log("Whole command is " + commandText);

    var searchUrl = url + searchUrlAppend + encodeURIComponent(commandText.trim());

    console.log(searchUrl);

    var questions = [];
    var question = {};

    client.registerMethod("jsonMethod", searchUrl, "GET");
    client.methods.jsonMethod(function (data, response) {

        if (data.items == null) {
            console.log("Unable to find any matching questions");
            return -1;
        }

        var questionId = data.items[0].question_id;
        console.log("Found questionId: " + questionId);

        var questionUrl = url + '/questions/' + questionId + questionAnswerUrlAppend;

        client.registerMethod("jsonMethod", questionUrl, "GET");
        client.methods.jsonMethod(function (data, response) {
            var questionTitle = data.items[0].title;

            question['question'] = questionTitle;
            question['questionId'] = questionId;

            console.log("Question Title - " + questionTitle);

            var answerUrl = url + '/questions/' + questionId + '/answers' + questionAnswerUrlAppend;

            client.registerMethod("jsonMethod", answerUrl , "GET");
            client.methods.jsonMethod(function (data, response) {

                var answers = data.items.slice(1, 3).map(function(item) {return {'answer': item.body, 'answerId': item.answer_id}});
                console.log("Found answers: ")

                answers.forEach(console.log);

                question['answers'] = answers;

                console.log("===========================");
                questions.push(question);
                console.log(JSON.stringify(questions));

                sendMessageByBot(event, questions);
            });
        });
    });
});
<commit_msg>Remove unnecessary date variables<commit_after>var config = require('./config.js');
var flock = require('flockos');
var express = require('express');
var store = require('./store.js');
var chrono = require('chrono-node');
var Mustache = require('mustache');
var fs = require('fs');
var util = require('util');
var request = require('request');
var Client = require('node-rest-client').Client;
var client = new Client();

var watcher = require('./watcher.js');

flock.appId = config.appId;
flock.appSecret = config.appSecret;

var url = 'https://api.stackexchange.com/2.2/';
var searchUrlAppend = 'search?order=desc&sort=votes&site=stackoverflow&intitle=';

var questionAnswerUrlAppend = '?site=stackoverflow&filter=withbody';

// Change this when you deploy locally
var baseUrl = 'http://3676f534.ngrok.io/';

var app = express();
app.use(flock.events.tokenVerifier);
app.post('/events', flock.events.listener);

app.set('view engine', 'ejs');

app.listen(8080, function() {
    console.log('Listening on 8080');
});

// Start the watcher function async
setTimeout(function(){
    watcher.startWatcher(function(userId, questionId) {
        flock.chat.sendMessage(config.botToken, {
            to: userId,
            text: "Question Id " + questionId + " has changed!"
        });
    });
}, 0);

app.get('/addToWatchList/:userId/:questionId',function(req,res){

    var userId = req.params.userId;
    var questionId = req.params.questionId;

    console.log("userId = " + userId + " questionId " + questionId );
    watcher.addWatcher(userId, questionId);

    res.send("Hi, we will be watching the question and notify you if any answers are added");
});

// TODO: Unimplemented method
app.get('/upvoteAnswer/:userId/:answerId', function(req, res){
    var userId = req.params.userId;
    var questionId = req.params.answerId;

    res.send('Thank you for upvoting! :)');
});

// Return a list of question titles that the user is watching
app.get('/list',function(req,res){

    var event = JSON.parse(req.query.flockEvent);

    res.set('Content-Type', 'text/html');

    var userId = event.userId;

    console.log( "userId = " + userId);
    var watchList = watcher.getWatchList(userId);

    var questionTitles = getQuestionTitles(watchList[userId], res);
});

// Given a list of question id's, return a list of the titles of those questions
function getQuestionTitles(watchList, res) {
    var questionTitles = [];

    var questionUrl = url + '/questions/';
    console.log(watchList);

    for(var i = 0; i < watchList.length-1; i++) {
        console.log(i);
        var questionId = watchList[i];
        questionUrl = questionUrl + questionId + ';' ;
    }

    questionUrl = questionUrl + watchList[watchList.length - 1]+questionAnswerUrlAppend;
    console.log(questionUrl);
    client.get(questionUrl, function (data, response) {
        console.log("Got question title for watch list!");
        console.log(data);

        questionTitles = data.items.map(function(q) {return q.title})

        console.log("questionTitles "  + questionTitles);
        var body = '

'
        var end = '

'
        for (i in questionTitles) {
            body += '
'+ questionTitles[i] + "
";
        }
        body += end;
        console.log(body);
        res.send(body);
    });

    return questionTitles;
}

function sendMessageByBot(event, questions) {

    var css = ".btn { line-height: 30px;display: inline-block;float:left;width: 200px;text-align: center; cursor:pointer; background: #0abe50; background-image: -webkit-linear-gradient(top, #0abe50, #0abe50); background-image: -moz-linear-gradient(top, #0abe50, #0abe50); background-image: -ms-linear-gradient(top, #0abe50, #0abe50); background-image: -o-linear-gradient(top, #0abe50, #0abe50); background-image: linear-gradient(to bottom, #0abe50, #0abe50); -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0px; font-family: Arial; color: #f2f2f2; font-size: 14px; padding: 5px 5px 5px 5px; text-decoration: none; }.btn:hover { background: #5ce08f; background-image: -webkit-linear-gradient(top, #5ce08f, #5ce08f); background-image: -moz-linear-gradient(top, #5ce08f, #5ce08f); background-image: -ms-linear-gradient(top, #5ce08f, #5ce08f); background-image: -o-linear-gradient(top, #5ce08f, #5ce08f); background-image: linear-gradient(to bottom, #5ce08f, #5ce08f); text-decoration: none; }" ;
    var start = '<!DOCTYPE html><title>Questions</title>' ;
    var end = '';

    console.log("Firing BOT! :D");

    questions.forEach(function(e){

        var linkToWatch = baseUrl + 'addToWatchList/' + event.userId + '/'+ e.questionId ;

        e.answers.forEach(function(d){
            var linkToUpvote = baseUrl + 'upvoteAnswer/' + event.userId + '/' + d.answerId ;

            var start = '<!DOCTYPE html><title>Questions</title>';
            start += '
' + e.question + '
Watch

' ;
            start += '
' + d.answer + '
' ;
            start += '

Upvote this answer' + end;
            body = start;
            console.log(body);
            flock.chat.sendMessage(config.botToken, {
                to: event.userId,
                attachments: [{
                            "title": "We found something relevant on stackoverflow",
                            "views": {
                                "html": { "inline": body, "width" : 400, "height" : 400}
                            }
                    }]
            });
        });
    });
}

flock.events.on('app.install', function (event, callback) {

    flock.chat.sendMessage(config.botToken, {
            to: event.userId,
            text: "Welcome to the FlockSo app! FlockSo is StackOverflow for FlockOs"
    });

    store.saveToken(event.userId, event.token);
    callback();
});

flock.events.on('client.slashCommand', function (event, callback) {

    var commandText = event.text;
    console.log("Whole command is " + commandText);

    var searchUrl = url + searchUrlAppend + encodeURIComponent(commandText.trim());

    console.log(searchUrl);

    var questions = [];
    var question = {};

    client.registerMethod("jsonMethod", searchUrl, "GET");
    client.methods.jsonMethod(function (data, response) {

        if (data.items == null) {
            console.log("Unable to find any matching questions");
            return -1;
        }

        var questionId = data.items[0].question_id;
        console.log("Found questionId: " + questionId);

        var questionUrl = url + '/questions/' + questionId + questionAnswerUrlAppend;

        client.registerMethod("jsonMethod", questionUrl, "GET");
        client.methods.jsonMethod(function (data, response) {
            var questionTitle = data.items[0].title;

            question['question'] = questionTitle;
            question['questionId'] = questionId;

            console.log("Question Title - " + questionTitle);

            var answerUrl = url + '/questions/' + questionId + '/answers' + questionAnswerUrlAppend;

            client.registerMethod("jsonMethod", answerUrl , "GET");
            client.methods.jsonMethod(function (data, response) {

                var answers = data.items.slice(1, 3).map(function(item) {return {'answer': item.body, 'answerId': item.answer_id}});
                console.log("Found answers: ")

                answers.forEach(console.log);

                question['answers'] = answers;

                console.log("===========================");
                questions.push(question);
                console.log(JSON.stringify(questions));

                sendMessageByBot(event, questions);
            });
        });
    });
});
<|endoftext|>


<commit_before>var fs = require('fs-extra');
var path = require('path');
var spawn = require('child_process').spawn;
var pump = require('pump');
var hyperquest = require('hyperquest');
var zlib = require('zlib');
var unzip = require('unzip');
var csvToVrt = require('csv-to-vrt');
var UploadStream = require('./lib/UploadStream');
var checkHash = require('./lib/checkHash');

var zipReg = /.zip$/i;
var csvReg = /(?:txt|csv)$/i;
var restrictedReg = /\.\.|\//;

function retrieve(program, callback){

  var scratchSpace = 'scratch/' + Math.random()*1e17;
  fs.mkdirsSync(scratchSpace);

  function wrappedCb(err, count){
    fs.remove(scratchSpace, function(e){
      if(err||e){
        if(callback) return callback(err||e);
        throw err||e;
      }

      if(callback) callback(err, count);
    });
  }

  if(!program.file) return wrappedCb(new Error('Must provide a metadata file with the -f option.'));

  var stringMatch = typeof program.match === 'string';
  var regMatch = typeof program.match === 'object';
  var restrictedFound = 0;

  var uploadStream;

  var data;

  try{
    data = JSON.parse(fs.readFileSync(program.file));
  }catch(err){
    return wrappedCb(err);
  }

  var recordCount = data.length;
  var processed = 0;

  function recordCallback(err){
    if(err) return wrappedCb(err);
    if(++processed === recordCount) wrappedCb(null, recordCount);
  }

  if(program.bucket) uploadStream = new UploadStream(program.bucket, program.profile);

  data.forEach(function(record){

    //Don't allow to traverse to other folders via data.json
    if(restrictedReg.test(record.name)){
      restrictedFound = 1;
      return wrappedCb(new Error('Invalid record name. Must not contain ".." or "/".'));
    }

    if(restrictedFound) return;

    //If the record is filtered, remove it from the count
    if(stringMatch && record.name.indexOf(program.match) === -1 ||
       regMatch && !program.match.test(record.name)
    ){
       if(--recordCount === processed){
         return wrappedCb(null, recordCount);
       }
       return recordCount;
    }

    var request = hyperquest(record.url);

    checkHash(request, record.hash, function(hashIsEqual, remoteHash){
      if(hashIsEqual){
        console.log('Remote file verified.');
        return;
      }
      request.unpipe();
      request.emit('error', new Error('The hash from ' + record.name + ' did not match the downloaded file\'s hash.\nRecord hash: ' + record.hash +'\nRemote hash: ' + remoteHash +'\n'));
    });

    request.on('error', recordCallback);

    if(zipReg.test(record.url)){
      //unzip stream can't be pumped
      request.pipe(unzip.Extract({path: path.join(scratchSpace, record.name)}))
       .on('close', function(){

        var unzipped = path.join(scratchSpace, record.name, record.file);

        if(csvReg.test(record.file)){
          csvToVrt(unzipped, record.sourceSrs, function(err, vrt){
            if(err) return recordCallback(err);
            handleStream(spawnOgr(vrt), record, recordCallback);
          });
        }else{
          handleStream(spawnOgr(unzipped), record, recordCallback);
        }
      })
      .on('error', recordCallback);
    }else{
      if(csvReg.test(record.file)){
        var csv = path.join(scratchSpace, record.file);
        var csvStream = fs.createWriteStream(csv);

        pump(request, csvStream, function(err){
          if(err) return recordCallback(err);

          csvToVrt(csv, record.sourceSrs, function(err, vrt){
            if(err) return recordCallback(err);
            handleStream(spawnOgr(vrt), record, recordCallback);
          });
        });

      }else{
        handleStream(spawnOgr(null, request), record, recordCallback);
      }
    }
  });

  function spawnOgr(file, stream){
    var child;

    if(stream){
      child = spawn('ogr2ogr', ['-f', 'CSV', '-t_srs', 'WGS84', '-lco', 'GEOMETRY=AS_XY', '/vsistdout/', '/vsistdin/']);
      pump(stream, child.stdin);
    }else{
      child = spawn('ogr2ogr', ['-f', 'CSV', '-t_srs', 'WGS84', '-lco', 'GEOMETRY=AS_XY', '/vsistdout/', file]);
    }
    return child.stdout;
  }

  function handleStream(stream, record, cb){
    if(!cb) cb = function(err){if(err) wrappedCb(err)}

    var endfile = path.join(program.directory, record.name + '.csv.gz');
    var zipStream = zlib.createGzip();
    var destStream;

    if(program.bucket){
      destStream = uploadStream.stream(endfile);
    }else{
      destStream = fs.createWriteStream(endfile);
    }

    pump(stream, zipStream, destStream, function(err){
      if(err) return cb(err);
      cb();
    });
  }

}

module.exports = retrieve;
<commit_msg>Switch to request to follow redirects<commit_after>var fs = require('fs-extra');
var path = require('path');
var spawn = require('child_process').spawn;
var pump = require('pump');
var request = require('request');
var zlib = require('zlib');
var unzip = require('unzip');
var csvToVrt = require('csv-to-vrt');
var UploadStream = require('./lib/UploadStream');
var checkHash = require('./lib/checkHash');

var zipReg = /.zip$/i;
var csvReg = /(?:txt|csv)$/i;
var restrictedReg = /\.\.|\//;

function retrieve(program, callback){

  var scratchSpace = 'scratch/' + Math.random()*1e17;
  fs.mkdirsSync(scratchSpace);

  function wrappedCb(err, count){
    fs.remove(scratchSpace, function(e){
      if(err||e){
        if(callback) return callback(err||e);
        throw err||e;
      }

      if(callback) callback(err, count);
    });
  }

  if(!program.file) return wrappedCb(new Error('Must provide a metadata file with the -f option.'));

  var stringMatch = typeof program.match === 'string';
  var regMatch = typeof program.match === 'object';
  var restrictedFound = 0;

  var uploadStream;

  var data;

  try{
    data = JSON.parse(fs.readFileSync(program.file));
  }catch(err){
    return wrappedCb(err);
  }

  var recordCount = data.length;
  var processed = 0;

  function recordCallback(err){
    if(err) return wrappedCb(err);
    if(++processed === recordCount) wrappedCb(null, recordCount);
  }

  if(program.bucket) uploadStream = new UploadStream(program.bucket, program.profile);

  data.forEach(function(record){

    //Don't allow to traverse to other folders via data.json
    if(restrictedReg.test(record.name)){
      restrictedFound = 1;
      return wrappedCb(new Error('Invalid record name. Must not contain ".." or "/".'));
    }

    if(restrictedFound) return;

    //If the record is filtered, remove it from the count
    if(stringMatch && record.name.indexOf(program.match) === -1 ||
       regMatch && !program.match.test(record.name)
    ){
       if(--recordCount === processed){
         return wrappedCb(null, recordCount);
       }
       return recordCount;
    }

    var req = request(record.url);

    checkHash(req, record.hash, function(hashIsEqual, remoteHash){
      if(hashIsEqual){
        console.log('Remote file verified.');
        return;
      }
      req.unpipe();
      req.emit('error', new Error('The hash from ' + record.name + ' did not match the downloaded file\'s hash.\nRecord hash: ' + record.hash +'\nRemote hash: ' + remoteHash +'\n'));
    });

    req.on('error', recordCallback);

    if(zipReg.test(record.url)){
      //unzip stream can't be pumped
      req.pipe(unzip.Extract({path: path.join(scratchSpace, record.name)}))
       .on('close', function(){

        var unzipped = path.join(scratchSpace, record.name, record.file);

        if(csvReg.test(record.file)){
          csvToVrt(unzipped, record.sourceSrs, function(err, vrt){
            if(err) return recordCallback(err);
            handleStream(spawnOgr(vrt), record, recordCallback);
          });
        }else{
          handleStream(spawnOgr(unzipped), record, recordCallback);
        }
      })
      .on('error', recordCallback);
    }else{
      if(csvReg.test(record.file)){
        var csv = path.join(scratchSpace, record.file);
        var csvStream = fs.createWriteStream(csv);

        pump(req, csvStream, function(err){
          if(err) return recordCallback(err);

          csvToVrt(csv, record.sourceSrs, function(err, vrt){
            if(err) return recordCallback(err);
            handleStream(spawnOgr(vrt), record, recordCallback);
          });
        });

      }else{
        handleStream(spawnOgr(null, req), record, recordCallback);
      }
    }
  });

  function spawnOgr(file, stream){
    var child;

    if(stream){
      child = spawn('ogr2ogr', ['-f', 'CSV', '-t_srs', 'WGS84', '-lco', 'GEOMETRY=AS_XY', '/vsistdout/', '/vsistdin/']);
      pump(stream, child.stdin);
    }else{
      child = spawn('ogr2ogr', ['-f', 'CSV', '-t_srs', 'WGS84', '-lco', 'GEOMETRY=AS_XY', '/vsistdout/', file]);
    }
    return child.stdout;
  }

  function handleStream(stream, record, cb){
    if(!cb) cb = function(err){if(err) wrappedCb(err)}

    var endfile = path.join(program.directory, record.name + '.csv.gz');
    var zipStream = zlib.createGzip();
    var destStream;

    if(program.bucket){
      destStream = uploadStream.stream(endfile);
    }else{
      destStream = fs.createWriteStream(endfile);
    }

    pump(stream, zipStream, destStream, function(err){
      if(err) return cb(err);
      cb();
    });
  }

}

module.exports = retrieve;
<|endoftext|>


<commit_before>/**
 * A Spot for Slack!
 */

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function (err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am a bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

/**
 * Configure the persistence options
 */

var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_slack_bot_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

/**
 * Are being run as an app or a custom integration? The initialization will differ, depending
 */

if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

var apiai = require('botkit-middleware-apiai')({
   token: process.env.APIAI_TOKEN
});
controller.middleware.receive.use(apiai.receive);

/**
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function (bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function (bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/**
 * Core bot logic goes here!
 */
// BEGIN EDITING HERE!

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

/*controller.hears('hello', ['mention', 'direct_mention','direct_message'], function (bot, message) {
    bot.reply(message, 'Hello!');
});*/

controller.hears(['hello'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'Hello!');
});

controller.hears(['goodbye'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'See you later alligator!');
});

controller.hears(['flights'], 'direct_message', apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   } else {
       bot.reply(message, "You requested to fly to " + message.entities['geo-city'] + " on " + message.entities['date']+".");
   }
});

controller.hears(['calculate'], ['direct_mention', 'mention', 'direct_message'], apiai.hears, function (bot,message) {
  var userID = message.user
  var user = "<@"+userID+">"
  if(message.fulfillment.speech !== '') {
    bot.reply(message, message.fulfillment.speech);
  } else if (message.entities['keyword'] == 'client') {
    var total = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var day_rate = (total / (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client is paying: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nThis is made up of: \nContractor day rate: *$"+day_rate+"* :tada: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead margin: *$"+margin+"* :moneybag:");
  } else if (message.entities['keyword'] == 'perm') {
    var salary = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var pkg = (salary * 1.095).toFixed(2);
    var fee = (pkg * client_rate).toFixed(2);
    bot.reply(message, user+"\nThe candidate package is *$"+pkg+"*. :the_horns: \nLookahead fee: *$"+fee+"* :moneybag:");
  } else {
    var day_rate = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var total = (day_rate * (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client will be charged: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead earn: *$"+margin+"* :moneybag:");
  }
});

controller.hears(['Dog'], ['direct_message','mention', 'direct_mention'], apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   }
});

controller.hears(['help'], ['direct_message','direct_mention','mention'], function (bot, message) {
   bot.reply(message, 'Here are some things you can ask me: \ncalculate candidate 1000 20: outputs the cost to the client and Lookahead\'s margin \ncalculate client 1300 20: reverses the prior calculation and gives you the day rate and margin \n calculate perm 100000 20: gives you the package total and placement fee \n');
});

/**
 * AN example of what could be:
 * Any un-handled direct mention gets a reaction and a pat response!
 */
//controller.on('direct_message,mention,direct_mention', function (bot, message) {
//    bot.api.reactions.add({
//        timestamp: message.ts,
//        channel: message.channel,
//        name: 'robot_face',
//    }, function (err) {
//        if (err) {
//            console.log(err)
//        }
//        bot.reply(message, 'I heard you loud and clear boss.');
//    });
//});
<commit_msg>Reorder commands and add fetch<commit_after>/**
 * A Spot for Slack!
 */

/**
 * Define a function for initiating a conversation on installation
 * With custom integrations, we don't have a way to find out who installed us, so we can't message them :(
 */

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({user: installer}, function (err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am a bot that has just joined your team');
                convo.say('You must now /invite me to a channel so that I can be of use!');
            }
        });
    }
}

/**
 * Configure the persistence options
 */

var config = {};
if (process.env.MONGOLAB_URI) {
    var BotkitStorage = require('botkit-storage-mongo');
    config = {
        storage: BotkitStorage({mongoUri: process.env.MONGOLAB_URI}),
    };
} else {
    config = {
        json_file_store: ((process.env.TOKEN)?'./db_slack_bot_ci/':'./db_slack_bot_a/'), //use a different name if an app or CI
    };
}

/**
 * Are being run as an app or a custom integration? The initialization will differ, depending
 */

if (process.env.TOKEN || process.env.SLACK_TOKEN) {
    //Treat this as a custom integration
    var customIntegration = require('./lib/custom_integrations');
    var token = (process.env.TOKEN) ? process.env.TOKEN : process.env.SLACK_TOKEN;
    var controller = customIntegration.configure(token, config, onInstallation);
} else if (process.env.CLIENT_ID && process.env.CLIENT_SECRET && process.env.PORT) {
    //Treat this as an app
    var app = require('./lib/apps');
    var controller = app.configure(process.env.PORT, process.env.CLIENT_ID, process.env.CLIENT_SECRET, config, onInstallation);
} else {
    console.log('Error: If this is a custom integration, please specify TOKEN in the environment. If this is an app, please specify CLIENTID, CLIENTSECRET, and PORT in the environment');
    process.exit(1);
}

var apiai = require('botkit-middleware-apiai')({
   token: process.env.APIAI_TOKEN
});
controller.middleware.receive.use(apiai.receive);

/**
 * A demonstration for how to handle websocket events. In this case, just log when we have and have not
 * been disconnected from the websocket. In the future, it would be super awesome to be able to specify
 * a reconnect policy, and do reconnections automatically. In the meantime, we aren't going to attempt reconnects,
 * WHICH IS A B0RKED WAY TO HANDLE BEING DISCONNECTED. So we need to fix this.
 *
 * TODO: fixed b0rked reconnect behavior
 */
// Handle events related to the websocket connection to Slack
controller.on('rtm_open', function (bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close', function (bot) {
    console.log('** The RTM api just closed');
    // you may want to attempt to re-open
});

/**
 * Core bot logic goes here!
 */
// BEGIN EDITING HERE!

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

/*controller.hears('hello', ['mention', 'direct_mention','direct_message'], function (bot, message) {
    bot.reply(message, 'Hello!');
});*/

controller.hears(['help'], ['direct_message','direct_mention','mention'], function (bot, message) {
   bot.reply(message, 'Here are some things you can ask me: \ncalculate candidate 1000 20: outputs the cost to the client and Lookahead\'s margin \ncalculate client 1300 20: reverses the prior calculation and gives you the day rate and margin \n calculate perm 100000 20: gives you the package total and placement fee \n');
});

controller.hears(['fetch'], ['direct_message','direct_mention','mention'], function (bot, message) {
   bot.reply(message, 'Stop trying to make fetch happen. It\'s never going to happen.');
});

controller.hears(['hello'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'Hello!');
});

controller.hears(['goodbye'], 'direct_message', apiai.hears, function (bot, message) {
   bot.reply(message, 'See you later alligator!');
});

controller.hears(['flights'], 'direct_message', apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   } else {
       bot.reply(message, "You requested to fly to " + message.entities['geo-city'] + " on " + message.entities['date']+".");
   }
});

controller.hears(['calculate'], ['direct_mention', 'mention', 'direct_message'], apiai.hears, function (bot,message) {
  var userID = message.user
  var user = "<@"+userID+">"
  if(message.fulfillment.speech !== '') {
    bot.reply(message, message.fulfillment.speech);
  } else if (message.entities['keyword'] == 'client') {
    var total = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var day_rate = (total / (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client is paying: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nThis is made up of: \nContractor day rate: *$"+day_rate+"* :tada: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead margin: *$"+margin+"* :moneybag:");
  } else if (message.entities['keyword'] == 'perm') {
    var salary = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var pkg = (salary * 1.095).toFixed(2);
    var fee = (pkg * client_rate).toFixed(2);
    bot.reply(message, user+"\nThe candidate package is *$"+pkg+"*. :the_horns: \nLookahead fee: *$"+fee+"* :moneybag:");
  } else {
    var day_rate = parseInt(message.entities['day_rate']);
    var client_rate = (parseInt(message.entities['rate']) / 100);
    var total = (day_rate * (1.1 + client_rate)).toFixed(2);
    var tax = (day_rate * 0.1).toFixed(2);
    var gst = (total * 0.1).toFixed(2);
    var margin = (total - tax - day_rate).toFixed(2);
    bot.reply(message, user+"\nThe client will be charged: *$"+total+"*, plus *$"+gst+"* GST. :the_horns: \nTax/Insurance: *$"+tax+"* :angry: \nLookahead earn: *$"+margin+"* :moneybag:");
  }
});

controller.hears(['Dog'], ['direct_message','mention', 'direct_mention'], apiai.hears, function (bot, message) {
   if(message.fulfillment.speech !== '') {
       bot.reply(message, message.fulfillment.speech);
   }
});

/**
 * AN example of what could be:
 * Any un-handled direct mention gets a reaction and a pat response!
 */
//controller.on('direct_message,mention,direct_mention', function (bot, message) {
//    bot.api.reactions.add({
//        timestamp: message.ts,
//        channel: message.channel,
//        name: 'robot_face',
//    }, function (err) {
//        if (err) {
//            console.log(err)
//        }
//        bot.reply(message, 'I heard you loud and clear boss.');
//    });
//});
<|endoftext|>


<commit_before>'use strict';
/**
*
* Script to run in browser context, will scan through title and alt tags
* look for attribute's hidden inside of those tags in the form:
* title="actual title | attribute name: attribute value | ..."
* will then remove those embedded attributes and stick them on the
* element.
*/
(function(){
  document.addEventListener('DOMContentLoaded', function(){

    var elements = [].slice.call(document.querySelectorAll('*[title*="~"], *[alt*="~"]')) || [];

    elements.forEach(function(e){

      ['title', 'alt'].forEach(function(name){

	var attrs = (e.getAttribute(name) || '').split('~');
	if(attrs.length === 0) return;

	// set initial content as actual attribute
	var entry = attrs.shift();
	e.setAttribute(name, entry.trim());

	// then look for the embedded attributes
	while(entry = (attrs.shift() || '').trim()){
	  var pair = entry.split(':');
	  e.setAttribute(pair.shift().trim(), (pair.shift() || '').trim());
	}
      });
    });
  });
})();

<commit_msg>fixed | -> ~ in comment use description<commit_after>'use strict';
/**
*
* Script to run in browser context, will scan through title and alt tags
* look for attribute's hidden inside of those tags in the form:
* title="actual title ~ attribute name: attribute value ~ ..."
* will then remove those embedded attributes and stick them on the
* element.
*/
(function(){
  document.addEventListener('DOMContentLoaded', function(){

    var elements = [].slice.call(document.querySelectorAll('*[title*="~"], *[alt*="~"]')) || [];

    elements.forEach(function(e){

      ['title', 'alt'].forEach(function(name){

	var attrs = (e.getAttribute(name) || '').split('~');
	if(attrs.length === 0) return;

	// set initial content as actual attribute
	var entry = attrs.shift();
	e.setAttribute(name, entry.trim());

	// then look for the embedded attributes
	while(entry = (attrs.shift() || '').trim()){
	  var pair = entry.split(':');
	  e.setAttribute(pair.shift().trim(), (pair.shift() || '').trim());
	}
      });
    });
  });
})();

<|endoftext|>


<commit_before>'use strict';

var childProcess = require('child_process');
var nodeBin = process.argv[0];

module.exports = sleep;
function sleep(milliseconds) {
  var start = Date.now();
  if (milliseconds !== Math.floor(milliseconds)) {
    throw new TypeError('sleep only accepts an integer number of milliseconds');
  } else if (milliseconds < 0) {
    throw new RangeError('sleep only accepts a positive number of milliseconds');
  } else if (milliseconds !== (milliseconds | 0)) {
    throw new RangeError('sleep duration out of range')
  }
  milliseconds = milliseconds | 0;

  var shouldEnd = start + milliseconds;
  childProcess.execFileSync(nodeBin, [ '-e',
    'setTimeout(function() {}, ' + shouldEnd + ' - Date.now());'
  ]);
  var end = Date.now();
  return end - start;
}
<commit_msg>Use timeout to handle very slow node on some platforms<commit_after>'use strict';

var childProcess = require('child_process');
var nodeBin = process.argv[0];

module.exports = sleep;
function sleep(milliseconds) {
  var start = Date.now();
  if (milliseconds !== Math.floor(milliseconds)) {
    throw new TypeError('sleep only accepts an integer number of milliseconds');
  } else if (milliseconds < 0) {
    throw new RangeError('sleep only accepts a positive number of milliseconds');
  } else if (milliseconds !== (milliseconds | 0)) {
    throw new RangeError('sleep duration out of range')
  }
  milliseconds = milliseconds | 0;

  var shouldEnd = start + milliseconds;
  try {
    childProcess.execFileSync(nodeBin, [ '-e',
      'setTimeout(function() {}, ' + shouldEnd + ' - Date.now());'
    ], {
      timeout: milliseconds,
    });
  } catch (ex) {
    if (ex.code !== 'ETIMEDOUT') {
      throw ex;
    }
  }
  var end = Date.now();
  return end - start;
}
<|endoftext|>


<commit_before>'use strict';

var _         = require('lodash');
var Bluebird  = require('bluebird');
var fs        = require('fs');
var Migration = require('./lib/migration');
var path      = require('path');
var redefine  = require('redefine');

var Umzug = module.exports = redefine.Class({
  constructor: function (options) {
    this.options = _.assign({
      storage:        'json',
      storageOptions: {},
      logging:        false,
      upName:         'up',
      downName:       'down'
    }, options);

    if (this.options.logging && !_.isFunction(this.options.logging)) {
      throw new Error('The logging-option should be either a function or false');
    }

    this.options.migrations = _.assign({
      params:  [],
      path:    path.resolve(process.cwd(), 'migrations'),
      pattern: /^\d+[\w-]+\.js$/,
      wrap:    function (fun) { return fun; }
    }, this.options.migrations);

    this.storage = this._initStorage();
  },

  execute: function (options) {
    var self = this;

    options = _.assign({
      migrations: [],
      method:     'up'
    }, options || {});

    return Bluebird
      .map(options.migrations, function (migration) {
        return self._findMigration(migration);
      })
      .then(function (migrations) {
        return _.assign({}, options, { migrations: migrations });
      })
      .then(function (options) {
        return Bluebird.each(options.migrations, function (migration) {
          var name = path.basename(migration.file, path.extname(migration.file));
          var startTime;
          return self
            ._wasExecuted(migration)
            .catch(function () {
              return false;
            })
            .then(function (executed) {
              return (typeof executed === 'undefined') ? true : executed;
            })
            .tap(function (executed) {
              if (!executed || (options.method === 'down')) {
                var fun    = (migration[options.method] || Bluebird.resolve);
                var params = self.options.migrations.params;

                if (typeof params === 'function') {
                  params = params();
                }

                if (options.method === 'up') {
                  self.log("== " + name + ": migrating =======");
                } else {
                  self.log("== " + name + ": reverting =======");
                }

                startTime = new Date();

                return fun.apply(migration, params);
              }
            })
            .then(function (executed) {
              if (!executed && (options.method === 'up')) {
                return Bluebird.resolve(self.storage.logMigration(migration.file));
              } else if (options.method === 'down') {
                return Bluebird.resolve(self.storage.unlogMigration(migration.file));
              }
            })
            .tap(function () {
              var duration = ((new Date() - startTime) / 1000).toFixed(3);
              if (options.method === 'up') {
                self.log("== " + name + ": migrated (" + duration +  "s)\n");
              } else {
                self.log("== " + name + ": reverted (" + duration +  "s)\n");
              }
            });
        });
      });
  },

  executed: function () {
    return Bluebird.resolve(this.storage.executed()).bind(this).map(function (file) {
      return new Migration(file);
    });
  },

  pending: function () {
    return this
      ._findMigrations()
      .bind(this)
      .then(function (all) {
        return Bluebird.join(all, this.executed());
      })
      .spread(function (all, executed) {
        var executedFiles = executed.map(function (migration) {
          return migration.file;
        });

        return all.filter(function (migration) {
          return executedFiles.indexOf(migration.file) === -1;
        }).sort(function (a, b) {
          if (a.file > b.file) {
            return 1;
          } else  if (a.file < b.file) {
            return -1;
          } else {
            return 0;
          }
        });
      });
  },

  up: function (options) {
    return this._run(true, options, this.pending.bind(this));
  },

  down: function (options) {
    var getExecuted = function () {
      return this.executed().bind(this).then(function(migrations) {
        return migrations.reverse();
      });
    }.bind(this);

    if (typeof options === 'undefined') {
      return getExecuted().bind(this).then(function (migrations) {
        return migrations[0]
          ? this.down(migrations[0].file)
          : Bluebird.resolve([]);
      });
    } else {
      return this._run(false, options, getExecuted.bind(this));
    }
  },

  _run: function(up, options, rest) {
    if (typeof options === 'string') {
      return this._run(up, [ options ]);
    } else if (Array.isArray(options)) {
      return Bluebird.resolve(options).bind(this)
        .map(function (migration) {
          return this._findMigration(migration)
        })
        .then(function (migrations) {
          return up ?
            this._arePending(migrations) :
            this._wereExecuted(migrations);
        })
        .then(function () {
          return this._run(up, { migrations: options });
        });
    }

    options = _.assign({
      to:         null,
      migrations: null
    }, options || {});

    if (options.migrations) {
      return this.execute({
        migrations: options.migrations,
        method: up ? 'up' : 'down'
      });
    } else {
      return rest().bind(this)
        .then(function (migrations) {
          var result = Bluebird.resolve().bind(this);

          if (options.to) {
            result = result
              .then(function () {
                // There must be a migration matching to options.to...
                return this._findMigration(options.to);
              })
              .then(function (migration) {
                // ... and it must be pending/executed.
                return up ?
                  this._isPending(migration) :
                  this._wasExecuted(migration);
              });
          }

          return result.then(function () {
            return Bluebird.resolve(migrations)
          });
        })
        .then(function (migrations) {
          return this._findMigrationsUntilMatch(options.to, migrations);
        })
        .then(function (migrationFiles) {
          return this._run(up, { migrations: migrationFiles });
        });
    }
  },

  log: function(message) {
    if (this.options.logging) {
      this.options.logging(message);
    }
  },

  _initStorage: function () {
    var Storage;

    try {
      Storage = require(__dirname + '/lib/storages/' + this.options.storage);
    } catch (e) {
      // We have not been able to find the storage locally.
      // Let's try to require a module instead.
    }

    try {
      Storage = Storage || require(this.options.storage);
    } catch (e) {
      throw new Error('Unable to resolve the storage: ' + this.options.storage + ', ' + e);
    }

    return new Storage(this.options);
  },

  _findMigrations: function () {
    return Bluebird
      .promisify(fs.readdir)(this.options.migrations.path)
      .bind(this)
      .filter(function (file) {
        return this.options.migrations.pattern.test(file);
      })
      .map(function (file) {
        return path.resolve(this.options.migrations.path, file);
      })
      .map(function (path) {
        return new Migration(path, this.options);
      });
  },

  _findMigration: function (needle) {
    return this
      ._findMigrations()
      .then(function (migrations) {
        return migrations.filter(function (migration) {
          return migration.testFileName(needle);
        })[0];
      })
      .then(function (migration) {
        if (migration) {
          return migration;
        } else {
          return Bluebird.reject(new Error('Unable to find migration: ' + needle));
        }
      });
  },

  _wasExecuted: function (_migration) {
    return this.executed().filter(function (migration) {
      return migration.testFileName(_migration.file);
    }).then(function(migrations) {
      if (migrations[0]) {
        return Bluebird.resolve();
      } else {
        return Bluebird.reject(new Error('Migration was not executed: ' + _migration.file));
      }
    });
  },

  _wereExecuted: function (migrationNames) {
    return Bluebird
      .resolve(migrationNames)
      .bind(this)
      .map(function (migration) {
        return this._wasExecuted(migration);
      });
  },

  _isPending: function (_migration) {
    return this.pending().filter(function (migration) {
      return migration.testFileName(_migration.file);
    }).then(function(migrations) {
      if (migrations[0]) {
        return Bluebird.resolve();
      } else {
        return Bluebird.reject(new Error('Migration is not pending: ' + _migration.file));
      }
    });
  },

  _arePending: function (migrationNames) {
    return Bluebird
      .resolve(migrationNames)
      .bind(this)
      .map(function (migration) {
        return this._isPending(migration);
      });
  },

  _findMigrationsUntilMatch: function (to, migrations) {
    return Bluebird.resolve(migrations)
      .map(function (migration) { return migration.file })
      .reduce(function (acc, migration) {
        if (acc.add) {
          acc.migrations.push(migration);

          if (to && (migration.indexOf(to) === 0)) {
            // Stop adding the migrations once the final migration
            // has been added.
            acc.add = false;
          }
        }

        return acc;
      }, { migrations: [], add: true })
      .get('migrations');
  }
});
<commit_msg>Changed argument of _run from up:boolean to method:enum{"up", "down"}<commit_after>'use strict';

var _         = require('lodash');
var Bluebird  = require('bluebird');
var fs        = require('fs');
var Migration = require('./lib/migration');
var path      = require('path');
var redefine  = require('redefine');

var Umzug = module.exports = redefine.Class({
  constructor: function (options) {
    this.options = _.assign({
      storage:        'json',
      storageOptions: {},
      logging:        false,
      upName:         'up',
      downName:       'down'
    }, options);

    if (this.options.logging && !_.isFunction(this.options.logging)) {
      throw new Error('The logging-option should be either a function or false');
    }

    this.options.migrations = _.assign({
      params:  [],
      path:    path.resolve(process.cwd(), 'migrations'),
      pattern: /^\d+[\w-]+\.js$/,
      wrap:    function (fun) { return fun; }
    }, this.options.migrations);

    this.storage = this._initStorage();
  },

  execute: function (options) {
    var self = this;

    options = _.assign({
      migrations: [],
      method:     'up'
    }, options || {});

    return Bluebird
      .map(options.migrations, function (migration) {
        return self._findMigration(migration);
      })
      .then(function (migrations) {
        return _.assign({}, options, { migrations: migrations });
      })
      .then(function (options) {
        return Bluebird.each(options.migrations, function (migration) {
          var name = path.basename(migration.file, path.extname(migration.file));
          var startTime;
          return self
            ._wasExecuted(migration)
            .catch(function () {
              return false;
            })
            .then(function (executed) {
              return (typeof executed === 'undefined') ? true : executed;
            })
            .tap(function (executed) {
              if (!executed || (options.method === 'down')) {
                var fun    = (migration[options.method] || Bluebird.resolve);
                var params = self.options.migrations.params;

                if (typeof params === 'function') {
                  params = params();
                }

                if (options.method === 'up') {
                  self.log("== " + name + ": migrating =======");
                } else {
                  self.log("== " + name + ": reverting =======");
                }

                startTime = new Date();

                return fun.apply(migration, params);
              }
            })
            .then(function (executed) {
              if (!executed && (options.method === 'up')) {
                return Bluebird.resolve(self.storage.logMigration(migration.file));
              } else if (options.method === 'down') {
                return Bluebird.resolve(self.storage.unlogMigration(migration.file));
              }
            })
            .tap(function () {
              var duration = ((new Date() - startTime) / 1000).toFixed(3);
              if (options.method === 'up') {
                self.log("== " + name + ": migrated (" + duration +  "s)\n");
              } else {
                self.log("== " + name + ": reverted (" + duration +  "s)\n");
              }
            });
        });
      });
  },

  executed: function () {
    return Bluebird.resolve(this.storage.executed()).bind(this).map(function (file) {
      return new Migration(file);
    });
  },

  pending: function () {
    return this
      ._findMigrations()
      .bind(this)
      .then(function (all) {
        return Bluebird.join(all, this.executed());
      })
      .spread(function (all, executed) {
        var executedFiles = executed.map(function (migration) {
          return migration.file;
        });

        return all.filter(function (migration) {
          return executedFiles.indexOf(migration.file) === -1;
        }).sort(function (a, b) {
          if (a.file > b.file) {
            return 1;
          } else  if (a.file < b.file) {
            return -1;
          } else {
            return 0;
          }
        });
      });
  },

  up: function (options) {
    return this._run('up', options, this.pending.bind(this));
  },

  down: function (options) {
    var getExecuted = function () {
      return this.executed().bind(this).then(function(migrations) {
        return migrations.reverse();
      });
    }.bind(this);

    if (typeof options === 'undefined') {
      return getExecuted().bind(this).then(function (migrations) {
        return migrations[0]
          ? this.down(migrations[0].file)
          : Bluebird.resolve([]);
      });
    } else {
      return this._run('down', options, getExecuted.bind(this));
    }
  },

  _run: function(method, options, rest) {
    if (typeof options === 'string') {
      return this._run(method, [ options ]);
    } else if (Array.isArray(options)) {
      return Bluebird.resolve(options).bind(this)
        .map(function (migration) {
          return this._findMigration(migration)
        })
        .then(function (migrations) {
          return method === 'up' ?
            this._arePending(migrations) :
            this._wereExecuted(migrations);
        })
        .then(function () {
          return this._run(method, { migrations: options });
        });
    }

    options = _.assign({
      to:         null,
      migrations: null
    }, options || {});

    if (options.migrations) {
      return this.execute({
        migrations: options.migrations,
        method: method
      });
    } else {
      return rest().bind(this)
        .then(function (migrations) {
          var result = Bluebird.resolve().bind(this);

          if (options.to) {
            result = result
              .then(function () {
                // There must be a migration matching to options.to...
                return this._findMigration(options.to);
              })
              .then(function (migration) {
                // ... and it must be pending/executed.
                return method === 'up' ?
                  this._isPending(migration) :
                  this._wasExecuted(migration);
              });
          }

          return result.then(function () {
            return Bluebird.resolve(migrations)
          });
        })
        .then(function (migrations) {
          return this._findMigrationsUntilMatch(options.to, migrations);
        })
        .then(function (migrationFiles) {
          return this._run(method, { migrations: migrationFiles });
        });
    }
  },

  log: function(message) {
    if (this.options.logging) {
      this.options.logging(message);
    }
  },

  _initStorage: function () {
    var Storage;

    try {
      Storage = require(__dirname + '/lib/storages/' + this.options.storage);
    } catch (e) {
      // We have not been able to find the storage locally.
      // Let's try to require a module instead.
    }

    try {
      Storage = Storage || require(this.options.storage);
    } catch (e) {
      throw new Error('Unable to resolve the storage: ' + this.options.storage + ', ' + e);
    }

    return new Storage(this.options);
  },

  _findMigrations: function () {
    return Bluebird
      .promisify(fs.readdir)(this.options.migrations.path)
      .bind(this)
      .filter(function (file) {
        return this.options.migrations.pattern.test(file);
      })
      .map(function (file) {
        return path.resolve(this.options.migrations.path, file);
      })
      .map(function (path) {
        return new Migration(path, this.options);
      });
  },

  _findMigration: function (needle) {
    return this
      ._findMigrations()
      .then(function (migrations) {
        return migrations.filter(function (migration) {
          return migration.testFileName(needle);
        })[0];
      })
      .then(function (migration) {
        if (migration) {
          return migration;
        } else {
          return Bluebird.reject(new Error('Unable to find migration: ' + needle));
        }
      });
  },

  _wasExecuted: function (_migration) {
    return this.executed().filter(function (migration) {
      return migration.testFileName(_migration.file);
    }).then(function(migrations) {
      if (migrations[0]) {
        return Bluebird.resolve();
      } else {
        return Bluebird.reject(new Error('Migration was not executed: ' + _migration.file));
      }
    });
  },

  _wereExecuted: function (migrationNames) {
    return Bluebird
      .resolve(migrationNames)
      .bind(this)
      .map(function (migration) {
        return this._wasExecuted(migration);
      });
  },

  _isPending: function (_migration) {
    return this.pending().filter(function (migration) {
      return migration.testFileName(_migration.file);
    }).then(function(migrations) {
      if (migrations[0]) {
        return Bluebird.resolve();
      } else {
        return Bluebird.reject(new Error('Migration is not pending: ' + _migration.file));
      }
    });
  },

  _arePending: function (migrationNames) {
    return Bluebird
      .resolve(migrationNames)
      .bind(this)
      .map(function (migration) {
        return this._isPending(migration);
      });
  },

  _findMigrationsUntilMatch: function (to, migrations) {
    return Bluebird.resolve(migrations)
      .map(function (migration) { return migration.file })
      .reduce(function (acc, migration) {
        if (acc.add) {
          acc.migrations.push(migration);

          if (to && (migration.indexOf(to) === 0)) {
            // Stop adding the migrations once the final migration
            // has been added.
            acc.add = false;
          }
        }

        return acc;
      }, { migrations: [], add: true })
      .get('migrations');
  }
});
<|endoftext|>