<commit_before>/*
	Pokemon Showdown Bot, for NodeJS

	By: Ecuacion
*/

try {
	require('sugar');

	global.colors = require('colors');
	global.sys = require('sys');
	global.fs = require('fs');
	global.path = require('path');
	global.PSClient = require('node-ps-client');
} catch (e) {
	console.log(e.stack);
	console.log("ERROR: missing dependencies, try 'npm install'");
	process.exit(-1);
}

console.log((
	'-----------------------------------------------\n' +
	'   Welcome to Pokemon Showdown Bot for Node!   \n' +
	'-----------------------------------------------\n'
).yellow);

global.Tools = require('./tools.js');

if (!fs.existsSync('./config.js')) {
	console.log("config.js does not exist - creating one with default settings...");
	fs.writeFileSync('./config.js', fs.readFileSync('./config-example.js'));
}

global.Config = require('./config.js');
Tools.checkConfig();

global.reloadConfig = function () {
	Tools.uncacheTree('./config.js');
	global.Config = require('./config.js');
	Tools.checkConfig();
	CommandParser.reloadTokens();
};

info('Loading globals');

/* Globals */

global.Formats = {};

global.Settings = require('./settings.js');

global.DataDownloader = require('./data-downloader.js');

global.CommandParser = require('./command-parser.js');

/* Features */

global.Features = {};

var featureList = fs.readdirSync('./features/');

featureList.forEach(function (feature) {
	if (fs.existsSync('./features/' + feature + '/index.js')) {
		try {
			var f = require('./features/' + feature + '/index.js');
			if (f.id) {
				Features[f.id] = f;
				ok("New feature: " + f.id + ' | ' + f.desc);
			} else {
				error("Failed to load feature: " + './features/' + feature);
			}
		} catch (e) {
			errlog(e.stack);
			error("Failed to load feature: " + './features/' + feature);
		}
	}
});

global.reloadFeatures = function () {
	var featureList = fs.readdirSync('./features/');
	var errs = [];
	featureList.forEach(function (feature) {
		if (fs.existsSync('./features/' + feature + '/index.js')) {
			try {
				Tools.uncacheTree('./features/' + feature + '/index.js');
				var f = require('./features/' + feature + '/index.js');
				if (f.id) {
					if (Features[f.id] && typeof Features[f.id].destroy === "function") Features[f.id].destroy();
					Features[f.id] = f;
					Features[f.id].init();
				}
			} catch (e) {
				errlog(e.stack);
				errs.push(feature);
			}
		}
	});
	return errs;
};

/* Bot creation and connection */

function botAfterConnect () {
	//join rooms
	if (typeof Config.rooms === "string") {
		joinByQueryRequest(Config.rooms);
	} else {
		var cmds = [];
		for (var i = 0; i < Config.rooms.length; i++) {
			cmds.push('|/join ' + Config.rooms[i]);
		}
		for (var i = 0; i < Config.initCmds.length; i++) {
			cmds.push(Config.initCmds[i]);
		}
		Bot.send(cmds, 2000);
	}
	DataDownloader.download();
}

function joinByQueryRequest(target) {
	if (target === 'official' || target === 'public' || target === 'all') {
		info('Joining ' + target + ' rooms');
	} else {
		error('Config.rooms, as a string must be "official", "public" or "all"');
		var cmds = [];
		for (var i = 0; i < Config.initCmds.length; i++) {
			cmds.push(Config.initCmds[i]);
		}
		Bot.send(cmds, 2000);
		return;
	}
	Bot.on('queryresponse', function (data) {
		data = data.split('|');
		if (data[0] === 'rooms') {
			data.splice(0, 1);
			var str = data.join('|');
			var cmds = [];
			try {
				var rooms = JSON.parse(str);
				var offRooms = [], publicRooms = [];
				if (rooms.official) {
					for (var i = 0; i < rooms.official.length; i++) {
						if (rooms.official[i].title) offRooms.push(toId(rooms.official[i].title));
					}
				}
				if (rooms.chat) {
					for (var i = 0; i < rooms.chat.length; i++) {
						if (rooms.chat[i].title) publicRooms.push(toId(rooms.chat[i].title));
					}
				}
				if (target === 'all' || target === 'official') {
					for (var i = 0; i < offRooms.length; i++) cmds.push('|/join ' + offRooms[i]);
				}
				if (target === 'all' || target === 'public') {
					for (var i = 0; i < publicRooms.length; i++) cmds.push('|/join ' + publicRooms[i]);
				}
			} catch (e) {}
			for (var i = 0; i < Config.initCmds.length; i++) {
				cmds.push(Config.initCmds[i]);
			}
			Bot.send(cmds, 2000);
			Bot.on('queryresponse', function () {return;});
		}
	});
	Bot.send('|/cmd rooms');
}

var opts = {
	serverid: Config.serverid,
	secprotocols: [],
	connectionTimeout: Config.connectionTimeout,
	loginServer: 'https://play.pokemonshowdown.com/~~' + Config.serverid + '/action.php',
	nickName: null,
	pass: null,
	retryLogin: false,
	autoConnect: false,
	autoReconnect: false,
	autoReconnectDelay: 0,
	autoJoin: [],
	showErrors: (Config.debug ? Config.debug.debug : true),
	debug: (Config.debug ? Config.debug.debug : true)
};

info('Connecting to server ' + Config.server + ':' + Config.port);

global.Bot = new PSClient(Config.server, Config.port, opts);

var connected = false;
Bot.on('connect', function (con) {
	ok('Connected to server ' + Config.serverid);
	connected = true;
	for (var f in Features) {
		try {
			Features[f].init();
		} catch (e) {
			errlog(e.stack);
			error("Feature Crash: " + f + " | " + sys.inspect(e));
		}
	}
});

Bot.on('formats', function (formats) {
	global.Formats = {};
	var formatsArr = formats.split('|');
	var commaIndex, sharpIndex, f, arg, formatData, spf, nCommas;
	for (var i = 0; i < formatsArr.length; i++) {
		commaIndex = formatsArr[i].indexOf(',');
		if (commaIndex === -1) {
			Formats[toId(formatsArr[i])] = {name: formatsArr[i], team: true, ladder: true, chall: true};
		} else if (commaIndex === 0) {
			continue;
		} else {
			f = formatsArr[i].substr(0, commaIndex);
			arg = formatsArr[i].substr(commaIndex);
			formatData = {name: f, team: true, ladder: true, chall: true};
			sharpIndex = arg.indexOf('#');
			if (sharpIndex >= 0) formatData.team = false;
			spf = arg.split(',');
			nCommas = 0;
			for (var k = 0; k < spf.length; k++) if (!spf[k]) nCommas++;
			if (nCommas === 2) formatData.ladder = false;
			else if (nCommas === 3) formatData.chall = false;
			Formats[toId(f)] = formatData;
		}
	}
});

Bot.on('challstr', function (challstr) {
	info('Received challstr, logging in...');
	if (!Config.nick) {
		Bot.rename('Bot ' + Tools.generateRandomNick(10));
	} else {
		Bot.rename(Config.nick, Config.pass);
	}
});

var retryingRename = false;
Bot.on('renamefailure', function (e) {
	if (e === -1)  {
		if (!Config.nick) {
			debug('Login failure - generating another random nickname');
			Bot.rename('Bot ' + Tools.generateRandomNick(10));
		} else {
			error('Login failure - name registered, invalid or no password given');
			if (!Bot.status.named) {
				info("Invalid nick + pass, using a random nickname");
				Config.nick = '';
				Bot.rename('Bot ' + Tools.generateRandomNick(10));
			}
		}
	} else {
		if (Config.autoReloginDelay) {
			error('Login failure, retrying in ' + (Config.autoReloginDelay / 1000) + ' seconds');
			retryingRename = true;
			setTimeout(function () {
				retryingRename = false;
				if (!Config.nick) {
					Bot.rename('Bot ' + Tools.generateRandomNick(10));
				} else {
					Bot.rename(Config.nick, Config.pass);
				}
			}, Config.autoReloginDelay);
		} else {
			error('Login failure');
		}
	}
});

Bot.on('rename', function (name, named) {
	if (named) {
		ok('Succesfully logged in as ' + name);
		if (!Config.nick) {
			if (Bot.roomcount > 0) return; // Namechange, not initial login
			botAfterConnect();
		} else if (toId(Config.nick) === toId(name)) {
			botAfterConnect();
		}
	}
});

/* Reconnect timer */

var reconnectTimer = null;
var reconnecting = false;
Bot.on('disconnect', function (e) {
	connected = false;
	if (Config.autoReconnectDelay) {
		if (reconnecting) return;
		reconnecting = true;
		error('Disconnected from server, retrying in ' + (Config.autoReconnectDelay / 1000) + ' seconds');
		reconnectTimer = setTimeout(function () {
			reconnecting = false;
			info('Connecting to server ' + Config.server + ':' + Config.port);
			Bot.connect();
		}, Config.autoReconnectDelay);
	} else {
		error('Disconnected from server, exiting...');
		process.exit(0);
	}
});

/* Commands */

Bot.on('chat', function (room, timeOff, by, msg) {
	CommandParser.parse(room, by, msg);
	Settings.reportSeen(by, room, 'c', []);
});

Bot.on('pm', function (by, msg) {
	CommandParser.parse(',' + by, by, msg);
});

Bot.on('userjoin', function (room, by) {
	Settings.reportSeen(by, room, 'j', []);
});

Bot.on('userleave', function (room, by) {
	Settings.reportSeen(by, room, 'l', []);
});

Bot.on('userrename', function (room, old, by) {
	Settings.reportSeen(old, room, 'n', [by]);
});

/* Features */

Bot.on('line', function (room, message, isIntro, spl) {
	for (var f in Features) {
		try {
			Features[f].parse(room, message, isIntro, spl);
		} catch (e) {
			errlog(e.stack);
			error("Feature Crash: " + f + " | " + sys.inspect(e));
			Features[f].disabled = true;
			Features[f].parse = function () {
				return true;
			};
			info("Feature " + f + " has been disabled");
		}
	}
});

/* Info and debug */

Bot.on('joinroom', function (room) {
	info('Joined room ' + room + ' [' + Bot.rooms[room].type + ']');
});

Bot.on('joinfailure', function (room, e, info) {
	info('Could not join ' + room + ': [' + e + '] ' + info);
});

Bot.on('message', function (msg) {
	recv(msg);
});

Bot.on('send', function (msg) {
	sent(msg);
});

Bot.connect();

/* Global Monitor */

var checkSystem = function () {
	var status = '';
	var issue = false;
	status += 'Connection Status: ';
	if (connected) {
		status += 'connected'.green;
		status += ' | Login Status: ';
		if (Bot.status.named) {
			status += Bot.status.nickName.green;
		} else if (retryingRename) {
			status += Bot.status.nickName.yellow;
		} else {
			status += Bot.status.nickName.red;
			issue = 'login';
		}
	} else if (reconnecting) {
		status += 'retrying'.yellow;
	} else {
		issue = 'connect';
		status += 'disconnected'.red;
	}
	monitor(status);
	if (issue) {
		switch (issue) {
			case 'connect':
				info("Monitor failed: Connaction issue. Reconnecting");
				Bot.connect();
				break;
			case 'login':
				info("Monitor failed: Login issue. Loging in a random username");
				Config.nick = '';
				Bot.rename('Bot ' + Tools.generateRandomNick(10));
				break;
		}
	}
};
var sysChecker = setInterval(checkSystem, 60 * 60 * 1000);
<commit_msg>Fix crash on joinfailure<commit_after>/*
	Pokemon Showdown Bot, for NodeJS

	By: Ecuacion
*/

try {
	require('sugar');

	global.colors = require('colors');
	global.sys = require('sys');
	global.fs = require('fs');
	global.path = require('path');
	global.PSClient = require('node-ps-client');
} catch (e) {
	console.log(e.stack);
	console.log("ERROR: missing dependencies, try 'npm install'");
	process.exit(-1);
}

console.log((
	'-----------------------------------------------\n' +
	'   Welcome to Pokemon Showdown Bot for Node!   \n' +
	'-----------------------------------------------\n'
).yellow);

global.Tools = require('./tools.js');

if (!fs.existsSync('./config.js')) {
	console.log("config.js does not exist - creating one with default settings...");
	fs.writeFileSync('./config.js', fs.readFileSync('./config-example.js'));
}

global.Config = require('./config.js');
Tools.checkConfig();

global.reloadConfig = function () {
	Tools.uncacheTree('./config.js');
	global.Config = require('./config.js');
	Tools.checkConfig();
	CommandParser.reloadTokens();
};

info('Loading globals');

/* Globals */

global.Formats = {};

global.Settings = require('./settings.js');

global.DataDownloader = require('./data-downloader.js');

global.CommandParser = require('./command-parser.js');

/* Features */

global.Features = {};

var featureList = fs.readdirSync('./features/');

featureList.forEach(function (feature) {
	if (fs.existsSync('./features/' + feature + '/index.js')) {
		try {
			var f = require('./features/' + feature + '/index.js');
			if (f.id) {
				Features[f.id] = f;
				ok("New feature: " + f.id + ' | ' + f.desc);
			} else {
				error("Failed to load feature: " + './features/' + feature);
			}
		} catch (e) {
			errlog(e.stack);
			error("Failed to load feature: " + './features/' + feature);
		}
	}
});

global.reloadFeatures = function () {
	var featureList = fs.readdirSync('./features/');
	var errs = [];
	featureList.forEach(function (feature) {
		if (fs.existsSync('./features/' + feature + '/index.js')) {
			try {
				Tools.uncacheTree('./features/' + feature + '/index.js');
				var f = require('./features/' + feature + '/index.js');
				if (f.id) {
					if (Features[f.id] && typeof Features[f.id].destroy === "function") Features[f.id].destroy();
					Features[f.id] = f;
					Features[f.id].init();
				}
			} catch (e) {
				errlog(e.stack);
				errs.push(feature);
			}
		}
	});
	return errs;
};

/* Bot creation and connection */

function botAfterConnect () {
	//join rooms
	if (typeof Config.rooms === "string") {
		joinByQueryRequest(Config.rooms);
	} else {
		var cmds = [];
		for (var i = 0; i < Config.rooms.length; i++) {
			cmds.push('|/join ' + Config.rooms[i]);
		}
		for (var i = 0; i < Config.initCmds.length; i++) {
			cmds.push(Config.initCmds[i]);
		}
		Bot.send(cmds, 2000);
	}
	DataDownloader.download();
}

function joinByQueryRequest(target) {
	if (target === 'official' || target === 'public' || target === 'all') {
		info('Joining ' + target + ' rooms');
	} else {
		error('Config.rooms, as a string must be "official", "public" or "all"');
		var cmds = [];
		for (var i = 0; i < Config.initCmds.length; i++) {
			cmds.push(Config.initCmds[i]);
		}
		Bot.send(cmds, 2000);
		return;
	}
	Bot.on('queryresponse', function (data) {
		data = data.split('|');
		if (data[0] === 'rooms') {
			data.splice(0, 1);
			var str = data.join('|');
			var cmds = [];
			try {
				var rooms = JSON.parse(str);
				var offRooms = [], publicRooms = [];
				if (rooms.official) {
					for (var i = 0; i < rooms.official.length; i++) {
						if (rooms.official[i].title) offRooms.push(toId(rooms.official[i].title));
					}
				}
				if (rooms.chat) {
					for (var i = 0; i < rooms.chat.length; i++) {
						if (rooms.chat[i].title) publicRooms.push(toId(rooms.chat[i].title));
					}
				}
				if (target === 'all' || target === 'official') {
					for (var i = 0; i < offRooms.length; i++) cmds.push('|/join ' + offRooms[i]);
				}
				if (target === 'all' || target === 'public') {
					for (var i = 0; i < publicRooms.length; i++) cmds.push('|/join ' + publicRooms[i]);
				}
			} catch (e) {}
			for (var i = 0; i < Config.initCmds.length; i++) {
				cmds.push(Config.initCmds[i]);
			}
			Bot.send(cmds, 2000);
			Bot.on('queryresponse', function () {return;});
		}
	});
	Bot.send('|/cmd rooms');
}

var opts = {
	serverid: Config.serverid,
	secprotocols: [],
	connectionTimeout: Config.connectionTimeout,
	loginServer: 'https://play.pokemonshowdown.com/~~' + Config.serverid + '/action.php',
	nickName: null,
	pass: null,
	retryLogin: false,
	autoConnect: false,
	autoReconnect: false,
	autoReconnectDelay: 0,
	autoJoin: [],
	showErrors: (Config.debug ? Config.debug.debug : true),
	debug: (Config.debug ? Config.debug.debug : true)
};

info('Connecting to server ' + Config.server + ':' + Config.port);

global.Bot = new PSClient(Config.server, Config.port, opts);

var connected = false;
Bot.on('connect', function (con) {
	ok('Connected to server ' + Config.serverid);
	connected = true;
	for (var f in Features) {
		try {
			Features[f].init();
		} catch (e) {
			errlog(e.stack);
			error("Feature Crash: " + f + " | " + sys.inspect(e));
		}
	}
});

Bot.on('formats', function (formats) {
	global.Formats = {};
	var formatsArr = formats.split('|');
	var commaIndex, sharpIndex, f, arg, formatData, spf, nCommas;
	for (var i = 0; i < formatsArr.length; i++) {
		commaIndex = formatsArr[i].indexOf(',');
		if (commaIndex === -1) {
			Formats[toId(formatsArr[i])] = {name: formatsArr[i], team: true, ladder: true, chall: true};
		} else if (commaIndex === 0) {
			continue;
		} else {
			f = formatsArr[i].substr(0, commaIndex);
			arg = formatsArr[i].substr(commaIndex);
			formatData = {name: f, team: true, ladder: true, chall: true};
			sharpIndex = arg.indexOf('#');
			if (sharpIndex >= 0) formatData.team = false;
			spf = arg.split(',');
			nCommas = 0;
			for (var k = 0; k < spf.length; k++) if (!spf[k]) nCommas++;
			if (nCommas === 2) formatData.ladder = false;
			else if (nCommas === 3) formatData.chall = false;
			Formats[toId(f)] = formatData;
		}
	}
});

Bot.on('challstr', function (challstr) {
	info('Received challstr, logging in...');
	if (!Config.nick) {
		Bot.rename('Bot ' + Tools.generateRandomNick(10));
	} else {
		Bot.rename(Config.nick, Config.pass);
	}
});

var retryingRename = false;
Bot.on('renamefailure', function (e) {
	if (e === -1)  {
		if (!Config.nick) {
			debug('Login failure - generating another random nickname');
			Bot.rename('Bot ' + Tools.generateRandomNick(10));
		} else {
			error('Login failure - name registered, invalid or no password given');
			if (!Bot.status.named) {
				info("Invalid nick + pass, using a random nickname");
				Config.nick = '';
				Bot.rename('Bot ' + Tools.generateRandomNick(10));
			}
		}
	} else {
		if (Config.autoReloginDelay) {
			error('Login failure, retrying in ' + (Config.autoReloginDelay / 1000) + ' seconds');
			retryingRename = true;
			setTimeout(function () {
				retryingRename = false;
				if (!Config.nick) {
					Bot.rename('Bot ' + Tools.generateRandomNick(10));
				} else {
					Bot.rename(Config.nick, Config.pass);
				}
			}, Config.autoReloginDelay);
		} else {
			error('Login failure');
		}
	}
});

Bot.on('rename', function (name, named) {
	if (named) {
		ok('Succesfully logged in as ' + name);
		if (!Config.nick) {
			if (Bot.roomcount > 0) return; // Namechange, not initial login
			botAfterConnect();
		} else if (toId(Config.nick) === toId(name)) {
			botAfterConnect();
		}
	}
});

/* Reconnect timer */

var reconnectTimer = null;
var reconnecting = false;
Bot.on('disconnect', function (e) {
	connected = false;
	if (Config.autoReconnectDelay) {
		if (reconnecting) return;
		reconnecting = true;
		error('Disconnected from server, retrying in ' + (Config.autoReconnectDelay / 1000) + ' seconds');
		reconnectTimer = setTimeout(function () {
			reconnecting = false;
			info('Connecting to server ' + Config.server + ':' + Config.port);
			Bot.connect();
		}, Config.autoReconnectDelay);
	} else {
		error('Disconnected from server, exiting...');
		process.exit(0);
	}
});

/* Commands */

Bot.on('chat', function (room, timeOff, by, msg) {
	CommandParser.parse(room, by, msg);
	Settings.reportSeen(by, room, 'c', []);
});

Bot.on('pm', function (by, msg) {
	CommandParser.parse(',' + by, by, msg);
});

Bot.on('userjoin', function (room, by) {
	Settings.reportSeen(by, room, 'j', []);
});

Bot.on('userleave', function (room, by) {
	Settings.reportSeen(by, room, 'l', []);
});

Bot.on('userrename', function (room, old, by) {
	Settings.reportSeen(old, room, 'n', [by]);
});

/* Features */

Bot.on('line', function (room, message, isIntro, spl) {
	for (var f in Features) {
		try {
			Features[f].parse(room, message, isIntro, spl);
		} catch (e) {
			errlog(e.stack);
			error("Feature Crash: " + f + " | " + sys.inspect(e));
			Features[f].disabled = true;
			Features[f].parse = function () {
				return true;
			};
			info("Feature " + f + " has been disabled");
		}
	}
});

/* Info and debug */

Bot.on('joinroom', function (room) {
	info('Joined room ' + room + ' [' + Bot.rooms[room].type + ']');
});

Bot.on('joinfailure', function (room, e, moreInfo) {
	info('Could not join ' + room + ': [' + e + '] ' + moreInfo);
});

Bot.on('message', function (msg) {
	recv(msg);
});

Bot.on('send', function (msg) {
	sent(msg);
});

Bot.connect();

/* Global Monitor */

var checkSystem = function () {
	var status = '';
	var issue = false;
	status += 'Connection Status: ';
	if (connected) {
		status += 'connected'.green;
		status += ' | Login Status: ';
		if (Bot.status.named) {
			status += Bot.status.nickName.green;
		} else if (retryingRename) {
			status += Bot.status.nickName.yellow;
		} else {
			status += Bot.status.nickName.red;
			issue = 'login';
		}
	} else if (reconnecting) {
		status += 'retrying'.yellow;
	} else {
		issue = 'connect';
		status += 'disconnected'.red;
	}
	monitor(status);
	if (issue) {
		switch (issue) {
			case 'connect':
				info("Monitor failed: Connaction issue. Reconnecting");
				Bot.connect();
				break;
			case 'login':
				info("Monitor failed: Login issue. Loging in a random username");
				Config.nick = '';
				Bot.rename('Bot ' + Tools.generateRandomNick(10));
				break;
		}
	}
};
var sysChecker = setInterval(checkSystem, 60 * 60 * 1000);
<|endoftext|>


<commit_before>var es = require('event-stream'),
		compile = require('jade').compile;

module.exports = function(options) {
	'use strict';

	options = options || {};

	function jade(file, callback) {
		var compiled = compile(String(file.contents), options);
		var newFile = {
			path: file.path,
			contents: options.client ? compiled : compiled(options.data)
		};

		callback(null, newFile);
	}

	return es.map(jade);
};
<commit_msg>wrap contents in buffer<commit_after>var es = require('event-stream'),
		compile = require('jade').compile;

module.exports = function(options) {
	'use strict';

	options = options || {};

	function jade(file, callback) {
		var compiled = compile(String(file.contents), options);
		var result = options.client ? compiled.toString() : compiled(options.data);
		file.contents = new Buffer(result);

		callback(null, file);
	}

	return es.map(jade);
};
<|endoftext|>


<commit_before>/**
 * @version 1.0.2
 * @module browser-tabs-lock
 * @copyright VRAI Labs Pvt. Ltd. 2019
 * @author Bhumil Sarvaiya <bhumilsarvaiya@gmail.com>
 * @license ISC
*/

/**
 * @constant
 * @type {string}
 * @default
 * @description All the locks taken by this package will have this as prefix
*/
const locksStorageKey = 'browser-tabs-lock';

/**
 * @constant
 * @type {string}
 * @default
 * @description Acts as pool of charactes used to generate random string
*/
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';

/**
 * @function delay
 * @param {number} milliseconds - How long the delay should be in terms of milliseconds
 * @returns {Promise<void>}
 */
const delay = (milliseconds) => {
    return new Promise( resolve => setTimeout(resolve, milliseconds) );
}

/**
 * @function generateRandomString
 * @returns {string}
 * @description returns random string which is 8 characters long
 */
const generateRandomString = () => {
    let randomstring = '';
    for (let i = 0; i < 8; i++) {
        const index = Math.floor(Math.random() * chars.length);
        randomstring += chars[index];
    }
    return randomstring;
}

/**
 * @class
 * @example
 * const tabId = TabsLock.getTabId();
 * let lockKey = 'lockKey';
 * if (await TabsLock.acquireLock(lockKey, tabId, 8000)) {
 *      // lock acquired
 *      // do something
 *      TabsLock.releaseLock(lockKey, tabId);
 * } else {
 *      // lock was not acquired
 * }
 */
class TabsLock {
    constructor () {}

    /**
     * @function getTabId
     * @memberof TabsLock
     * @returns {string}
     * @static
     * @description Generates an id which will be unique for the browser tab
     */
    static getTabId () {
        this.lockCorrector();
        return  Date.now().toString() + generateRandomString()
    }

    /**
     * @async
     * @function acquireLock
     * @memberof TabsLock
     * @param {string} lockKey - Key for which the lock is being acquired
     * @param {string} tabId - Unique id for the browser tab that is currently trying to acquire the lock
     * @param {number} [timeout=5000] - Maximum time for which the function will wait to acquire the lock
     * @returns {boolean}
     * @static
     * @description Will return true if lock is being acuired, else false.
     *              Also the lock can be acquired for maximum 10 secs
     */
    static async acquireLock (lockKey, tabId, timeout=5000) {
        let currentTime = Date.now();
        const maxTime = currentTime + timeout;
        const storageKey = `${locksStorageKey}-${lockKey}`;
        const storage = window.localStorage;
        while (currentTime < maxTime) {
            let lockObj = storage.getItem(storageKey);
            if (lockObj === null) {
                const timeoutKey = `${tabId}-${lockKey}-${iat}`;
                storage.setItem(storageKey, JSON.stringify({
                    tabId,
                    iat: currentTime,
                    timeoutKey
                }));
                await delay(50);
                let lockObjPostDelay = storage.getItem(storageKey);
                if (lockObjPostDelay !== null) {
                    lockObjPostDelay = JSON.parse(lockObjPostDelay);
                    if (lockObjPostDelay.tabId === tabId && lockObjPostDelay.iat === currentTime) {
                        window[timeoutKey] = setTimeout(() => {
                            this.releaseLock(lockKey, tabId, currentTime);
                        }, 10000);
                        return true;
                    }
                }
            } else {
                await delay(30);
            }
            currentTime = Date.now();
        }
        return false;
    }

    /**
     * @function releaseLock
     * @memberof TabsLock
     * @param {string} lockKey - Key for which lock is being released
     * @param {string} tabId - Unique id for the browser tab for which the lock is being released
     * @param {number} [iat=null] - Time at which lock was acquired.
     *                              Will not be null only if called via setTimeout from acquireLock
     * @returns {void}
     * @static
     * @description Release a lock.
     */
    static releaseLock (lockKey, tabId, iat=null) {
        const storage = window.localStorage;
        const storageKey = `${locksStorageKey}-${lockKey}`;
        let lockObj = storage.getItem(storageKey);
        if (lockObj === null) {
            return;
        }
        lockObj = JSON.parse(lockObj);
        if (lockObj.tabId === tabId && (iat === null || lockObj.iat === iat)) {
            const timeoutKey = `${tabId}-${lockKey}-${iat}`;
            storage.removeItem(storageKey);
            clearTimeout(window[timeoutKey]);
        }
    }

    /**
     * @function lockCorrector
     * @memberof TabsLock
     * @returns {void}
     * @static
     * @description If a lock is acquired by a tab and the tab is closed before the lock is
     *              released, this function will release those locks
     */
    static lockCorrector () {
        setTimeout(() => {
            const minAllowedTime = Date.now() - 10000;
            const storage = window.localStorage;
            const keys = Object.keys(storage);
            for (let i = 0; i < keys.length; i++) {
                const lockKey = keys[i];
                if (lockKey.includes(locksStorageKey)) {
                    let lockObj = storage.getItem(lockKey);
                    if (lockObj !== null) {
                        lockObj = JSON.parse(lockObj);
                        if (lockObj.iat < minAllowedTime) {
                            storage.removeItem(lockKey);
                        }
                    }
                }
            }
            this.lockCorrector();
        }, 1000);
    }
}

module.exports = TabsLock;<commit_msg>author added<commit_after>/**
 * @version 1.0.3
 * @module browser-tabs-lock
 * @copyright VRAI Labs Pvt. Ltd. 2019
 * @author Bhumil Sarvaiya <bhumilsarvaiya@gmail.com>
 * @author Rishabh Poddar <rishabhpoddar@gmail.com>
 * @license ISC
*/

/**
 * @constant
 * @type {string}
 * @default
 * @description All the locks taken by this package will have this as prefix
*/
const locksStorageKey = 'browser-tabs-lock';

/**
 * @constant
 * @type {string}
 * @default
 * @description Acts as pool of charactes used to generate random string
*/
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';

/**
 * @function delay
 * @param {number} milliseconds - How long the delay should be in terms of milliseconds
 * @returns {Promise<void>}
 */
const delay = (milliseconds) => {
    return new Promise( resolve => setTimeout(resolve, milliseconds) );
}

/**
 * @function generateRandomString
 * @returns {string}
 * @description returns random string which is 8 characters long
 */
const generateRandomString = () => {
    let randomstring = '';
    for (let i = 0; i < 8; i++) {
        const index = Math.floor(Math.random() * chars.length);
        randomstring += chars[index];
    }
    return randomstring;
}

/**
 * @class
 * @example
 * const tabId = TabsLock.getTabId();
 * let lockKey = 'lockKey';
 * if (await TabsLock.acquireLock(lockKey, tabId, 8000)) {
 *      // lock acquired
 *      // do something
 *      TabsLock.releaseLock(lockKey, tabId);
 * } else {
 *      // lock was not acquired
 * }
 */
class TabsLock {
    constructor () {}

    /**
     * @function getTabId
     * @memberof TabsLock
     * @returns {string}
     * @static
     * @description Generates an id which will be unique for the browser tab
     */
    static getTabId () {
        this.lockCorrector();
        return  Date.now().toString() + generateRandomString()
    }

    /**
     * @async
     * @function acquireLock
     * @memberof TabsLock
     * @param {string} lockKey - Key for which the lock is being acquired
     * @param {string} tabId - Unique id for the browser tab that is currently trying to acquire the lock
     * @param {number} [timeout=5000] - Maximum time for which the function will wait to acquire the lock
     * @returns {boolean}
     * @static
     * @description Will return true if lock is being acuired, else false.
     *              Also the lock can be acquired for maximum 10 secs
     */
    static async acquireLock (lockKey, tabId, timeout=5000) {
        let currentTime = Date.now();
        const maxTime = currentTime + timeout;
        const storageKey = `${locksStorageKey}-${lockKey}`;
        const storage = window.localStorage;
        while (currentTime < maxTime) {
            let lockObj = storage.getItem(storageKey);
            if (lockObj === null) {
                const timeoutKey = `${tabId}-${lockKey}-${iat}`;
                storage.setItem(storageKey, JSON.stringify({
                    tabId,
                    iat: currentTime,
                    timeoutKey
                }));
                await delay(50);
                let lockObjPostDelay = storage.getItem(storageKey);
                if (lockObjPostDelay !== null) {
                    lockObjPostDelay = JSON.parse(lockObjPostDelay);
                    if (lockObjPostDelay.tabId === tabId && lockObjPostDelay.iat === currentTime) {
                        window[timeoutKey] = setTimeout(() => {
                            this.releaseLock(lockKey, tabId, currentTime);
                        }, 10000);
                        return true;
                    }
                }
            } else {
                await delay(30);
            }
            currentTime = Date.now();
        }
        return false;
    }

    /**
     * @function releaseLock
     * @memberof TabsLock
     * @param {string} lockKey - Key for which lock is being released
     * @param {string} tabId - Unique id for the browser tab for which the lock is being released
     * @param {number} [iat=null] - Time at which lock was acquired.
     *                              Will not be null only if called via setTimeout from acquireLock
     * @returns {void}
     * @static
     * @description Release a lock.
     */
    static releaseLock (lockKey, tabId, iat=null) {
        const storage = window.localStorage;
        const storageKey = `${locksStorageKey}-${lockKey}`;
        let lockObj = storage.getItem(storageKey);
        if (lockObj === null) {
            return;
        }
        lockObj = JSON.parse(lockObj);
        if (lockObj.tabId === tabId && (iat === null || lockObj.iat === iat)) {
            const timeoutKey = `${tabId}-${lockKey}-${iat}`;
            storage.removeItem(storageKey);
            clearTimeout(window[timeoutKey]);
        }
    }

    /**
     * @function lockCorrector
     * @memberof TabsLock
     * @returns {void}
     * @static
     * @description If a lock is acquired by a tab and the tab is closed before the lock is
     *              released, this function will release those locks
     */
    static lockCorrector () {
        setTimeout(() => {
            const minAllowedTime = Date.now() - 10000;
            const storage = window.localStorage;
            const keys = Object.keys(storage);
            for (let i = 0; i < keys.length; i++) {
                const lockKey = keys[i];
                if (lockKey.includes(locksStorageKey)) {
                    let lockObj = storage.getItem(lockKey);
                    if (lockObj !== null) {
                        lockObj = JSON.parse(lockObj);
                        if (lockObj.iat < minAllowedTime) {
                            storage.removeItem(lockKey);
                        }
                    }
                }
            }
            this.lockCorrector();
        }, 1000);
    }
}

module.exports = TabsLock;<|endoftext|>


<commit_before>var util = require('util');
var EventEmitter = require('events').EventEmitter;

var PACKET_CONF = 0x55;
var ACK_CONF = 0x33;
var FIN_CONF = 0x16;

var ACK_CMD = 0x00;
var FIRMWARE_CMD = 0x01;
var IR_TX_CMD = 0x02;
var IR_RX_AVAIL_CMD = 0x03;
var IR_RX_CMD = 0x04;
var RX_START_CMD = 0x05;
var RX_STOP_CMD = 0x06;
var MAX_SIGNAL_DURATION = 200;

var Infrared = function(hardware, callback) {

  this.spi = hardware.SPI({clockSpeed : 1000, mode:2});
  this.chipSelect = hardware.gpio(1);
  this.reset = hardware.gpio(2);
  this.irq = hardware.gpio(3);
  this.transmitting = false;
  this.listening = false;
  this.chipSelect.output().high();
  this.reset.output().high();
  this.irq.output().low();

  var self = this;

  console.log('is this thing even on?');

  // If we get a new listener
  this.on('newListener', function(event) {
    // And they are listening for rx data and we haven't been yet
    if (event == "data" && !this.listeners(event).length) {
        self.setListening(1);
    }
  });

  this.on('removeListener', function(event) {
    // If this was for the rx data event and there aren't any more listeners
    if (event == "data" && !this.listeners(event).length) {
        self.setListening(0);
    }
  });

  // Make sure we can communicate with the module
  this.establishCommunication(3, function(err, version) {
    console.log('established...');
    if (err) {
      setImmediate( function() {
        // Emit an error event
        self.emit('error');
      });
    }
    else {
      setImmediate( function() {
        // Emit a ready event
        self.emit('ready');
         // Start listening for IRQ interrupts
        self.irq.watch('high', self.IRQHandler.bind(self));
      });
    }

    // Make sure we aren't gathering rx data until someone is listening.
    self.setListening(0);

    // Complete the setup
    if (callback) {
      callback(err, self);
    }
  });
};

util.inherits(Infrared, EventEmitter);

Infrared.prototype.IRQHandler = function() {
  var self = this;
  // If we are not in the middle of transmitting
  if (!self.transmitting) {
    // Receive the durations
    self.fetchRXDurations( function fetched() {
    // Start listening for IRQ interrupts again
    self.irq.watch('high', self.IRQHandler.bind(self));
    });
  } else {
    // If we are, check back in a little bit
    setTimeout(self.IRQHandler.bind(self), 500);
  }
};

Infrared.prototype.setListening = function(set, callback) {
  var self = this;

  var cmd = set ? RX_START_CMD : RX_STOP_CMD;

  var response = this.SPITransfer(new Buffer([cmd, 0x00, 0x00]));

  self.validateResponse(response, [PACKET_CONF, cmd], function(valid) {

    if (!valid) {
        return callback && callback(new Error("Invalid response on setting rx on/off."));
    }
    else {
      self.listening = set ? true : false;

      if (callback) {
        callback();
      }
    }
  });
};

Infrared.prototype.fetchRXDurations = function(callback) {
  var self = this;
  // We have to pull chip select high in case we were in the middle of something else

  // this.chipSelect.high();
  this.SPITransfer(new Buffer([IR_RX_AVAIL_CMD, 0x00, 0x00, 0x00]), function(response) {
    // DO something smarter than this eventually

    self.validateResponse(response, [PACKET_CONF, IR_RX_AVAIL_CMD, 1], function(valid) {
      if (valid) {
        var numInt16 = response[3];

        // (We have two bytes per element...);
        var numBytes = numInt16 * 2;

        var rxHeader = [IR_RX_CMD, 0x00, 0x00];
        var packet = rxHeader.concat(new Array(numBytes));

        // Push the stop bit on there.
        packet.push(FIN_CONF);

        packet = new Buffer(packet);

        self.SPITransfer(packet, function(response) {

          var fin = response[response.length-1];

          if (fin != FIN_CONF) {
            console.log("Warning: Received Packet Out of Frame.");

            if (callback) {
              callback();
            }
          }

          if (err) {
            if (callback) {
              callback();
            }
            return;// console.log("Issue sending dummy bytes...");
          }

          else {
            // Remove the header echoes at the beginning
            var buf = response.slice(rxHeader.length, response.length);

            // Emit the buffer
            self.emit('data', buf);

            if (callback) {
              callback();
            }
          }
        });
      }
    });
  });
};

Infrared.prototype.sendRawSignal = function(frequency, signalDurations, callback) {
  if (frequency <= 0) {
    if (callback) {
      callback(new Error("Invalid frequency. Must be greater than zero. Works best between 36-40."));
    }

    return;
  }
  else if (signalDurations.length > MAX_SIGNAL_DURATION) {
    if (callback) {
      callback(new Error("Invalid buffer length. Must be between 1 and ", MAX_SIGNAL_DURATION));
    }

    return;
  }
  else {

    this.transmitting = true;

    var self = this;

    // Make the packet
    var tx = this.constructTXPacket(frequency, signalDurations);

    // Send it over
    this.SPITransfer(tx, function(response) {

      self.transmitting = false;
      var err;

      // If there was an error already, set immediate on the callback
      if (!self.validateResponse(response, [PACKET_CONF, IR_TX_CMD, frequency, signalDurations.length/2])) {
          err = new Error("Invalid response from raw signal packet: " + response);
      }

      if (callback) {
        callback(err);
      }
    });
  }
};

Infrared.prototype.constructTXPacket = function(frequency, signalDurations) {
  // Create array
  var tx = [];
  // Add command
  tx.push(IR_TX_CMD);
  // Frequency of PWN
  tx.push(frequency);

  // Add length of signal durations in terms of int16s
  tx.push(signalDurations.length/2);

  // For each signal duration
  for (var i = 0; i < signalDurations.length; i++) {
      // Send upper and lower bits
      tx.push(signalDurations.readUInt8(i));
  }
  // Put a dummy bit to get the last echo
  tx.push(0x00);

  // Put the finish confirmation
  tx.push(FIN_CONF);

  tx = new Buffer(tx);

  // return
  return tx;
};

Infrared.prototype.establishCommunication = function(retries, callback){
  var self = this;
  // Grab the firmware version
  self.getFirmwareVersion(function(err, version) {
    // If it didn't work
    if (err) {
      // Subtract number of retries
      retries--;
      // If there are no more retries possible
      if (!retries) {
        // Throw an error and return
        return callback && callback(new Error("Can't connect with module..."));
      }
      // Else call recursively
      else {
        self.establishCommunication(retries, callback);
      }
    }
    // If there was no error
    else {
      // Connected successfully
      self.connected = true;
      // Call callback with version
      if (callback) {
        callback(null, version);
      }
    }
  });
};

Infrared.prototype.getFirmwareVersion = function(callback) {
  var self = this;

  self.SPITransfer(new Buffer([FIRMWARE_CMD, 0x00, 0x00]), function(response) {
    if (err) {
      return callback(err, null);
    }
    else if (self.validateResponse(response, [false, FIRMWARE_CMD]) && response.length === 3)
    {
      if (callback) {
        callback(null, response[2]);
      }
    }
    else
    {
      if (callback) {
        callback(new Error("Error retrieving Firmware Version"));
      }
    }
  });
};

Infrared.prototype.validateResponse = function(values, expected, callback) {

  var res = true;

  for (var index = 0; index < expected.length; index++) {

    if (expected[index] === false) continue;

    if (expected[index] != values[index]) {
      res = false;
      break;
    }
  }

  if (callback) {
    callback(res);
  }

  return res;
};

Infrared.prototype.SPITransfer = function(data, callback) {

  // Pull Chip select down prior to transfer
  this.chipSelect.low();

  // Send over the data
  var ret = this.spi.transferSync(data);

  // Pull chip select back up
  this.chipSelect.high();

  // Call any callbacks
  if (callback) {
    callback(ret);
  }

  // Return the data
  return ret;
};

exports.Infrared = Infrared;
exports.use = function (hardware, callback) {
    return new Infrared(hardware, callback);
};<commit_msg>Fixes bug where data emitted always had a stop bit<commit_after>var util = require('util');
var EventEmitter = require('events').EventEmitter;

var PACKET_CONF = 0x55;
var ACK_CONF = 0x33;
var FIN_CONF = 0x16;

var ACK_CMD = 0x00;
var FIRMWARE_CMD = 0x01;
var IR_TX_CMD = 0x02;
var IR_RX_AVAIL_CMD = 0x03;
var IR_RX_CMD = 0x04;
var RX_START_CMD = 0x05;
var RX_STOP_CMD = 0x06;
var MAX_SIGNAL_DURATION = 200;

var Infrared = function(hardware, callback) {

  this.spi = hardware.SPI({clockSpeed : 1000, mode:2});
  this.chipSelect = hardware.gpio(1);
  this.reset = hardware.gpio(2);
  this.irq = hardware.gpio(3);
  this.transmitting = false;
  this.listening = false;
  this.chipSelect.output().high();
  this.reset.output().high();
  this.irq.output().low();

  var self = this;

  console.log('is this thing even on?');

  // If we get a new listener
  this.on('newListener', function(event) {
    // And they are listening for rx data and we haven't been yet
    if (event == "data" && !this.listeners(event).length) {
        self.setListening(1);
    }
  });

  this.on('removeListener', function(event) {
    // If this was for the rx data event and there aren't any more listeners
    if (event == "data" && !this.listeners(event).length) {
        self.setListening(0);
    }
  });

  // Make sure we can communicate with the module
  this.establishCommunication(3, function(err, version) {
    if (err) {
      setImmediate( function() {
        // Emit an error event
        self.emit('error');
      });
    }
    else {
      setImmediate( function() {
        // Emit a ready event
        self.emit('ready');
         // Start listening for IRQ interrupts
        self.irq.watch('high', self.IRQHandler.bind(self));
      });
    }

    // Make sure we aren't gathering rx data until someone is listening.
    self.setListening(0);

    // Complete the setup
    if (callback) {
      callback(err, self);
    }
  });
};

util.inherits(Infrared, EventEmitter);

Infrared.prototype.IRQHandler = function() {
  var self = this;
  // If we are not in the middle of transmitting
  if (!self.transmitting) {
    // Receive the durations
    self.fetchRXDurations( function fetched() {
    // Start listening for IRQ interrupts again
    self.irq.watch('high', self.IRQHandler.bind(self));
    });
  } else {
    // If we are, check back in a little bit
    setTimeout(self.IRQHandler.bind(self), 500);
  }
};

Infrared.prototype.setListening = function(set, callback) {
  var self = this;

  var cmd = set ? RX_START_CMD : RX_STOP_CMD;

  var response = this.SPITransfer(new Buffer([cmd, 0x00, 0x00]));

  self.validateResponse(response, [PACKET_CONF, cmd], function(valid) {

    if (!valid) {
        return callback && callback(new Error("Invalid response on setting rx on/off."));
    }
    else {
      self.listening = set ? true : false;

      if (callback) {
        callback();
      }
    }
  });
};

Infrared.prototype.fetchRXDurations = function(callback) {
  var self = this;
  // We have to pull chip select high in case we were in the middle of something else

  // this.chipSelect.high();
  this.SPITransfer(new Buffer([IR_RX_AVAIL_CMD, 0x00, 0x00, 0x00]), function(response) {
    // DO something smarter than this eventually

    self.validateResponse(response, [PACKET_CONF, IR_RX_AVAIL_CMD, 1], function(valid) {
      if (valid) {
        var numInt16 = response[3];

        // (We have two bytes per element...);
        var numBytes = numInt16 * 2;

        var rxHeader = [IR_RX_CMD, 0x00, 0x00];
        var packet = rxHeader.concat(new Array(numBytes));

        // Push the stop bit on there.
        packet.push(FIN_CONF);

        packet = new Buffer(packet);

        self.SPITransfer(packet, function(response) {

          var fin = response[response.length-1];

          if (fin != FIN_CONF) {
            console.warn("Warning: Received Packet Out of Frame.");

            if (callback) {
              callback();
            }
          }

          else {
            // Remove the header echoes at the beginning
            var buf = response.slice(rxHeader.length, response.length-1);

            // Emit the buffer
            self.emit('data', buf);

            if (callback) {
              callback();
            }
          }
        });
      }
    });
  });
};

Infrared.prototype.sendRawSignal = function(frequency, signalDurations, callback) {
  if (frequency <= 0) {
    if (callback) {
      callback(new Error("Invalid frequency. Must be greater than zero. Works best between 36-40."));
    }

    return;
  }
  else if (signalDurations.length > MAX_SIGNAL_DURATION) {
    if (callback) {
      callback(new Error("Invalid buffer length. Must be between 1 and ", MAX_SIGNAL_DURATION));
    }

    return;
  }
  else {

    this.transmitting = true;

    var self = this;

    // Make the packet
    var tx = this.constructTXPacket(frequency, signalDurations);

    // Send it over
    this.SPITransfer(tx, function(response) {

      self.transmitting = false;
      var err;

      // If there was an error already, set immediate on the callback
      if (!self.validateResponse(response, [PACKET_CONF, IR_TX_CMD, frequency, signalDurations.length/2])) {
          err = new Error("Invalid response from raw signal packet: " + response);
      }

      if (callback) {
        callback(err);
      }
    });
  }
};

Infrared.prototype.constructTXPacket = function(frequency, signalDurations) {
  // Create array
  var tx = [];
  // Add command
  tx.push(IR_TX_CMD);
  // Frequency of PWN
  tx.push(frequency);

  // Add length of signal durations in terms of int16s
  tx.push(signalDurations.length/2);

  // For each signal duration
  for (var i = 0; i < signalDurations.length; i++) {
      // Send upper and lower bits
      tx.push(signalDurations.readUInt8(i));
  }
  // Put a dummy bit to get the last echo
  tx.push(0x00);

  // Put the finish confirmation
  tx.push(FIN_CONF);

  tx = new Buffer(tx);

  // return
  return tx;
};

Infrared.prototype.establishCommunication = function(retries, callback){
  var self = this;
  // Grab the firmware version
  self.getFirmwareVersion(function(err, version) {
    // If it didn't work
    if (err) {
      // Subtract number of retries
      retries--;
      // If there are no more retries possible
      if (!retries) {
        // Throw an error and return
        return callback && callback(new Error("Can't connect with module..."));
      }
      // Else call recursively
      else {
        self.establishCommunication(retries, callback);
      }
    }
    // If there was no error
    else {
      // Connected successfully
      self.connected = true;
      // Call callback with version
      if (callback) {
        callback(null, version);
      }
    }
  });
};

Infrared.prototype.getFirmwareVersion = function(callback) {
  var self = this;

  self.SPITransfer(new Buffer([FIRMWARE_CMD, 0x00, 0x00]), function(response) {
    if (err) {
      return callback(err, null);
    }
    else if (self.validateResponse(response, [false, FIRMWARE_CMD]) && response.length === 3)
    {
      if (callback) {
        callback(null, response[2]);
      }
    }
    else
    {
      if (callback) {
        callback(new Error("Error retrieving Firmware Version"));
      }
    }
  });
};

Infrared.prototype.validateResponse = function(values, expected, callback) {

  var res = true;

  for (var index = 0; index < expected.length; index++) {

    if (expected[index] === false) continue;

    if (expected[index] != values[index]) {
      res = false;
      break;
    }
  }

  if (callback) {
    callback(res);
  }

  return res;
};

Infrared.prototype.SPITransfer = function(data, callback) {

  // Pull Chip select down prior to transfer
  this.chipSelect.low();

  // Send over the data
  var ret = this.spi.transferSync(data);

  // Pull chip select back up
  this.chipSelect.high();

  // Call any callbacks
  if (callback) {
    callback(ret);
  }

  // Return the data
  return ret;
};

exports.Infrared = Infrared;
exports.use = function (hardware, callback) {
    return new Infrared(hardware, callback);
};<|endoftext|>


<commit_before>(function nodePluginBootstrap(window) {
  var lt = window.lt;
  var path = require('path');

  var root = lt.objs.plugins.adjust_path(''); console.log('ROOT', root);
  if(!root) {
    throw new Error("plugin could not be found by LT:", root);
  }

  var ltrap = require(path.join(root, 'node_modules', 'ltrap'))({
    root: root,
    nwRequire: require,
    window: window,
  });

  var pkg = ltrap.require('package');

  if(!lt.js_plugins) {
    lt.js_plugins = {};
  }

  // Initiate new plugin instance.
  var plugin = ltrap.require(path.join('lib', pkg.name));
  lt.js_plugins[pkg.name] = plugin;
})(window);
<commit_msg>Removes stray console.log<commit_after>(function nodePluginBootstrap(window) {
  var lt = window.lt;
  var path = require('path');

  var root = lt.objs.plugins.adjust_path('');
  if(!root) {
    throw new Error("plugin could not be found by LT:", root);
  }

  var ltrap = require(path.join(root, 'node_modules', 'ltrap'))({
    root: root,
    nwRequire: require,
    window: window,
  });

  var pkg = ltrap.require('package');

  if(!lt.js_plugins) {
    lt.js_plugins = {};
  }

  // Initiate new plugin instance.
  var plugin = ltrap.require(path.join('lib', pkg.name));
  lt.js_plugins[pkg.name] = plugin;
})(window);
<|endoftext|>


<commit_before>/**
 * node.js `fs` stubbing mixin for sinon-doublist.
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
/*global beforeEach:false afterEach:false*/
'use strict';

var sinonDoublistFs = module.exports = function(fs, test) {
  if (is.string(test)) {
    globalInjector[test](fs);
    return;
  }

  if (is.Function(fs.exists.restore)) {
    return;
  }

  Object.keys(mixin).forEach(function(method) {
    test[method] = bind(test, mixin[method]);
  });

  test.fsStub = test.stub(fs);

  console.log('equal', fs === test.fsStub);

  test.fsStub.Stats.restore();

  test.fsStub.exists.callsArgWith(1, false);
  test.fsStub.existsSync.returns(false);

  Object.keys(fsStubMap).forEach(function(method) {
    test.fsStub[method].restore();
    test.fsStub[method] = test.stub(fs, method, fsStubMap[method]);
  });
};

sinonDoublistFs.require = require; // Give tests access to component loader.

var is = require('is');
var bind = require('bind');
var configurable = require('configurable.js');
var fileStubMap = {};
var mixin = {};
var fsStubMap = {};

mixin.stubFile = function(name) {
  if (!is.string(name) || name.trim() === '') {
    throw new Error('invalid stubFile() name: ' + JSON.stringify(name));
  }

  var fileStub = new FileStub(this.fsStub);
  return fileStub.set('name', name).set('sandbox', this);
};

fsStubMap.writeFile = function(filename, data, cb) {
  var stub = fileStubMap[filename];
  if (stub) {
    stub.buffer(data);
  }
  cb(null);
};

fsStubMap.writeFileSync = function(filename, data) {
  var stub = fileStubMap[filename];
  if (stub) {
    stub.buffer(data);
  }
};

/**
 * An entry in the map of stubbed files.
 */
function FileStub(fsStub) {
  this.settings = {
    fsStub: fsStub,
    sandbox: {},
    name: '',
    readdir: false, // Or array of paths.
    stats: { // From fs.Stats example in manual.
      dev: 2114,
      ino: 48064969,
      mode: 33188,
      nlink: 1,
      uid: 85,
      gid: 100,
      rdev: 0,
      size: 527,
      blksize: 4096,
      blocks: 8,
      atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
      mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
      ctime: 'Mon, 10 Oct 2011 23:24:11 GMT'
    }
  };
}

configurable(FileStub.prototype);

FileStub.prototype.buffer = function(buffer) {
  if (is.string(buffer)) {
    buffer = new Buffer(buffer);
  }
  var fsStub = this.get('fsStub');
  fsStub.readFileSync.withArgs(this.get('name')).returns(buffer);
  fsStub.readFile.withArgs(this.get('name')).yields(null, buffer);
  return this;
};

FileStub.prototype.readdir = function(files) {
  if (false !== files && !is.array(files))  { // Avoid silent test misconfig.
    throw new Error('invalid readdir config: ' + JSON.stringify(files));
  }
  return this.set('readdir', files);
};

FileStub.prototype.stat = function(key, val) {
  var stats = this.get('stats');
  if (typeof stats[key] === 'undefined') { // Avoid silent test misconfig.
    throw new Error('invalid fs.Stats property: ' + key);
  }
  stats[key] = val;
  return this.set('stats', stats);
};

/**
 * Finalize the fs stubs based on collected settings.
 */
FileStub.prototype.make = function() {
  var name = this.get('name');

  fileStubMap[name] = this; // For later lookup in fake writeFile, etc.

  var fsStub = this.get('fsStub');
  var stubMany = this.get('sandbox').stubMany;

  fsStub.exists.withArgs(name).yields(true);
  fsStub.existsSync.withArgs(name).returns(true);

  var stats = this.get('stats');
  var statsObj = new fsStub.Stats();
  Object.keys(stats).forEach(function(key) {
    statsObj[key] = stats[key];
  });
  var paths = this.get('readdir');
  var isDir = is.array(paths);

  stubMany(statsObj, 'isDirectory').isDirectory.returns(isDir);
  stubMany(statsObj, 'isFile').isFile.returns(!isDir);

  fsStub.stat.withArgs(this.get('name')).yields(null, statsObj);
  fsStub.statSync.withArgs(this.get('name')).returns(statsObj);

  if (isDir) {
    fsStub.readdir.withArgs(this.get('name')).yields(null, paths);
    fsStub.readdirSync.withArgs(this.get('name')).returns(paths);
  } else {
    var err = new Error('ENOTDIR, not a directory ' + name);
    fsStub.readdir.withArgs(this.get('name')).throws(err)
    fsStub.readdirSync.withArgs(this.get('name')).throws(err);
  }
};

var globalInjector = {
  mocha: function(fs) {
    beforeEach(function(hookDone) {
      sinonDoublistFs(fs, this);
      hookDone();
    });
  }
};
<commit_msg>Add comments<commit_after>/**
 * node.js `fs` stubbing mixin for sinon-doublist.
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
/*global beforeEach:false afterEach:false*/
'use strict';

/**
 * @param {object} fs Pre-loaded module.
 * @param {string|object} test Test context (with sinonDoublist sandbox),
 *   or the name of a supported runner to globally configure.
 *   Supported: 'mocha'
 */
var sinonDoublistFs = module.exports = function(fs, test) {
  if (is.string(test)) {
    globalInjector[test](fs);
    return;
  }

  if (is.Function(fs.exists.restore)) { // Already doubled.
    return;
  }

  Object.keys(mixin).forEach(function(method) { // stubFile(), etc.
    test[method] = bind(test, mixin[method]);
  });

  test.fsStub = test.stub(fs);

  // Regain access to original constructor for fs.stat() stubbing.
  test.fsStub.Stats.restore();

  // Force all existence checks to fail by default.
  test.fsStub.exists.callsArgWith(1, false);
  test.fsStub.existsSync.returns(false);

  // Replace initial full-object stubs with some custom ones.
  Object.keys(customFsStub).forEach(function(method) {
    test.fsStub[method].restore();
    test.fsStub[method] = test.stub(fs, method, customFsStub[method]);
  });
};

sinonDoublistFs.require = require; // Give tests access to component loader.

var is = require('is');
var bind = require('bind');
var configurable = require('configurable.js');
var fileStubMap = {};
var mixin = {};
var customFsStub = {};

/**
 * Begin configuring a file stub.
 *
 * @param {string} name File/directory name.
 * @return {object} this
 */
mixin.stubFile = function(name) {
  if (!is.string(name) || name.trim() === '') {
    throw new Error('invalid stubFile() name: ' + JSON.stringify(name));
  }

  var fileStub = new FileStub(this.fsStub);
  return fileStub.set('name', name).set('sandbox', this);
};

/**
 * Replace fs.writeFile() in order to capture passed buffers for later
 * access by fs.readFile*().
 *
 * @param {string} filename
 * @param {string|object} data String or Buffer instance.
 * @param {function} cb
 */
customFsStub.writeFile = function(filename, data, cb) {
  var stub = fileStubMap[filename];
  if (stub) {
    stub.buffer(data);
  }
  cb(null);
};

/**
 * Replace fs.writeFile() in order to capture passed buffers for later
 * access by fs.readFile*().
 *
 * @param {string} filename
 * @param {string|object} data String or Buffer instance.
 */
customFsStub.writeFileSync = function(filename, data) {
  var stub = fileStubMap[filename];
  if (stub) {
    stub.buffer(data);
  }
};

/**
 * An entry in the map of stubbed files.
 */
function FileStub(fsStub) {
  this.settings = {
    name: '',
    readdir: false, // Or array of paths.
    fsStub: fsStub,
    sandbox: {}, // sinonDoublist sandbox.
    stats: { // From fs.Stats example in manual.
      dev: 2114,
      ino: 48064969,
      mode: 33188,
      nlink: 1,
      uid: 85,
      gid: 100,
      rdev: 0,
      size: 527,
      blksize: 4096,
      blocks: 8,
      atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
      mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
      ctime: 'Mon, 10 Oct 2011 23:24:11 GMT'
    }
  };
}

configurable(FileStub.prototype);

/**
 * Set the buffer to be returned by readFile*() calls.
 *
 * @param {string|object} buffer String or Buffer instance.
 * @return this
 */
FileStub.prototype.buffer = function(buffer) {
  if (is.string(buffer)) {
    buffer = new Buffer(buffer);
  }
  var fsStub = this.get('fsStub');
  fsStub.readFileSync.withArgs(this.get('name')).returns(buffer);
  fsStub.readFile.withArgs(this.get('name')).yields(null, buffer);
  return this;
};

/**
 * Set readdir*() results.
 *
 * @param {boolean|array} Array of strings, or false to revert to default isFile=true.
 * @return this
 */
FileStub.prototype.readdir = function(files) {
  if (false !== files && !is.array(files))  { // Avoid silent test misconfig.
    throw new Error('invalid readdir config: ' + JSON.stringify(files));
  }
  return this.set('readdir', files);
};

/**
 * Set an fs.Stats property.
 *
 * @param {string} key Ex. 'size' or 'gid'.
 * @param {mixed} val
 * @return this
 */
FileStub.prototype.stat = function(key, val) {
  var stats = this.get('stats');
  if (typeof stats[key] === 'undefined') { // Avoid silent test misconfig.
    throw new Error('invalid fs.Stats property: ' + key);
  }
  stats[key] = val;
  return this.set('stats', stats);
};

/**
 * Finalize the fs.{exists,stat,etc.} stubs based on collected settings.
 */
FileStub.prototype.make = function() {
  var name = this.get('name');

  fileStubMap[name] = this; // For later lookup in fake writeFile, etc.

  var fsStub = this.get('fsStub');
  var stubMany = this.get('sandbox').stubMany;

  fsStub.exists.withArgs(name).yields(true);
  fsStub.existsSync.withArgs(name).returns(true);

  var stats = this.get('stats');
  var statsObj = new fsStub.Stats();
  Object.keys(stats).forEach(function(key) {
    statsObj[key] = stats[key];
  });

  var paths = this.get('readdir');
  var isDir = is.array(paths);

  stubMany(statsObj, 'isDirectory').isDirectory.returns(isDir);
  stubMany(statsObj, 'isFile').isFile.returns(!isDir);

  fsStub.stat.withArgs(this.get('name')).yields(null, statsObj);
  fsStub.statSync.withArgs(this.get('name')).returns(statsObj);

  if (isDir) {
    fsStub.readdir.withArgs(this.get('name')).yields(null, paths);
    fsStub.readdirSync.withArgs(this.get('name')).returns(paths);
  } else {
    var err = new Error('ENOTDIR, not a directory ' + name);
    fsStub.readdir.withArgs(this.get('name')).throws(err)
    fsStub.readdirSync.withArgs(this.get('name')).throws(err);
  }
};

var globalInjector = {
  mocha: function(fs) {
    beforeEach(function(hookDone) {
      sinonDoublistFs(fs, this);
      hookDone();
    });
  }
};
<|endoftext|>


<commit_before>'use strict';

var each = require('each-async');
var fs = require('fs-extra');
var path = require('path');
var trashdir = require('xdg-trashdir');
var uuid = require('uuid');

/**
 * Safely move files and directories to trash on Linux
 *
 * @param {String} src
 * @param {Function} cb
 * @api private
 */

function trash(src, cb) {
	trashdir(src.replace(/\s/g, '\\ '), function (err, dir) {
		if (err) {
			if (err.code === 'ENOENT') {
				err.noStack = true;
			}

			cb(err);
			return;
		}

		var name = uuid.v4();
		var dest = path.join(dir, 'files', name);
		var info = path.join(dir, 'info', name + '.trashinfo');

		var msg = [
			'[Trash Info]',
			'Path=' + src.replace(/\s/g, '%20'),
			'DeletionDate=' + new Date().toISOString()
		].join('\n');

		fs.move(src, dest, { mkdirp: true }, function (err) {
			if (err) {
				cb(err);
				return;
			}

			fs.outputFile(info, msg, function (err) {
				if (err) {
					cb(err);
					return;
				}

				cb(null, { path: dest, info: info });
			});
		});
	});
}

/**
 * Module exports
 *
 * @param {Array} paths
 * @param {Function} cb
 * @api public
 */

module.exports = function (paths, cb) {
	var files = [];
	cb = cb || function () {};

	if (process.platform !== 'linux') {
		cb(new Error('Only Linux systems are supported'));
		return;
	}

	if (!Array.isArray(paths)) {
		cb(new Error('`paths` is required'));
		return;
	}

	paths = paths.map(function (p) {
		return path.resolve(String(p));
	});

	each(paths, function (path, i, next) {
		trash(path, function (err, file) {
			if (err) {
				next(err);
				return;
			}

			files.push(file);
			next();
		});
	}, function (err) {
		if (err) {
			cb(err);
			return;
		}

		cb(null, files);
	});
};
<commit_msg>File names with spaces are handled in `mount-point`<commit_after>'use strict';

var each = require('each-async');
var fs = require('fs-extra');
var path = require('path');
var trashdir = require('xdg-trashdir');
var uuid = require('uuid');

/**
 * Safely move files and directories to trash on Linux
 *
 * @param {String} src
 * @param {Function} cb
 * @api private
 */

function trash(src, cb) {
	trashdir(src, function (err, dir) {
		if (err) {
			if (err.code === 'ENOENT') {
				err.noStack = true;
			}

			cb(err);
			return;
		}

		var name = uuid.v4();
		var dest = path.join(dir, 'files', name);
		var info = path.join(dir, 'info', name + '.trashinfo');

		var msg = [
			'[Trash Info]',
			'Path=' + src.replace(/\s/g, '%20'),
			'DeletionDate=' + new Date().toISOString()
		].join('\n');

		fs.move(src, dest, { mkdirp: true }, function (err) {
			if (err) {
				cb(err);
				return;
			}

			fs.outputFile(info, msg, function (err) {
				if (err) {
					cb(err);
					return;
				}

				cb(null, { path: dest, info: info });
			});
		});
	});
}

/**
 * Module exports
 *
 * @param {Array} paths
 * @param {Function} cb
 * @api public
 */

module.exports = function (paths, cb) {
	var files = [];
	cb = cb || function () {};

	if (process.platform !== 'linux') {
		cb(new Error('Only Linux systems are supported'));
		return;
	}

	if (!Array.isArray(paths)) {
		cb(new Error('`paths` is required'));
		return;
	}

	paths = paths.map(function (p) {
		return path.resolve(String(p));
	});

	each(paths, function (path, i, next) {
		trash(path, function (err, file) {
			if (err) {
				next(err);
				return;
			}

			files.push(file);
			next();
		});
	}, function (err) {
		if (err) {
			cb(err);
			return;
		}

		cb(null, files);
	});
};
<|endoftext|>


<commit_before>/**
 * GitBit Bot for Slack !
 * @author: Vasu Jain
 */

// Libraries
var https = require('https');
var BotConfig = require('./config.json');

var authTokenEncrypted = BotConfig.auth_token;
//var authTokenDecrypted = "token " + Buffer.from(authTokenEncrypted, 'base64').toString("ascii");

// For Node.js v5.11.1 and below
var buf = new Buffer(authTokenEncrypted, 'base64');
var authTokenDecrypted = "token " + buf.toString("ascii");

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({
            user: installer
        }, function(err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am GitBit 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
    };
}

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

/**
 * 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, message) {
    console.log('** The RTM api just connected!');
                getListOfAllGithubReposInOrg(bot, message);

});

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

// Core bot logic !
controller.on('bot_channel_join', function(bot, message) {
    bot.reply(message, "Thank you for inviting me to your Slack Channel!")
});

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

controller.hears('pr (.*)', ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    var repo = message.match[1];
    if (typeof repo !== 'undefined' && repo) {
        var githubRepo = BotConfig.repos[repo];
        var flagZeroPRComment = false;
        if (githubRepo) {
            flagZeroPRComment = true;
            githubGetPullRequest(githubRepo, bot, message, flagZeroPRComment);
        } else if (repo == 'custom') {
            for (var r in BotConfig.repos) {
                githubGetPullRequest(BotConfig.repos[r], bot, message, flagZeroPRComment);
            }
        } else if (repo == 'all') {
            getListOfAllGithubReposInOrg(bot, message);
        } else {
            bot.reply(message, "Invalid Repo or Repo not configured");
        }
    } else {
        bot.reply(message, "Repo is undefined -- Invalid request or Repo not configured");
    }
});

// Make a POST call to GITHUB API to fetch all OPEN PR's
function githubGetPullRequest(repo, bot, message, flagZeroPRComment) {
    console.log("Making a POST call to GITHUB API to fetch all OPEN PR's...");
    var request = require('request');
    var url = BotConfig.github_api_url + 'repos/' + BotConfig.repo_org + repo + '/pulls?state=open';
    console.log(url);
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        parseAndResponse(body, bot, message, repo, flagZeroPRComment);
    });
}

// Parse the pull response json and extract PR#, Title, User out of it.
function parseAndResponse(body, bot, message, repo, flagZeroPRComment) {
    console.log("Parsing the pull response json and extracting PR#, Title, User out of it...");
    var repoSource = BotConfig.repo_org + repo + " Open Pull Requests : ";
    var response = "";
    var obj = JSON.parse(body);
    var objLength = obj.length;
    if (objLength == 0) {
        if (!BotConfig.disable_zero_pr_repo) { //if false, then only display Repo with Zero PR
            response = repoSource;
            if (flagZeroPRComment) {
                response += "No open PR's @ the moment ! Are you guys coding ?"
            } else {
                response += "0."
            }
            bot.reply(message, response);
        }
    } else {
        response = repoSource;
        for (var i = 0; i < objLength; i++) {
            response += "\nPR # " + obj[i].number + " - " + obj[i].title + " by " + obj[i].user.login;
            bot.reply(message, response);
        }
    }
    console.log(response);
    console.log("parseAndResponse for " + repo + " with " + objLength + " PR'(s) executed successfully.");
}

// Getting list of all Github Repos in an Org. Can be 100+. For the initial phase only top 100 results will display
function getListOfAllGithubReposInOrg(bot, message) {
    console.log("Getting list of all Github Repos in an Org. Can be 100+....");
    var ghArray = new Array();
    var url = BotConfig.github_api_url + 'orgs/' + BotConfig.repo_org + 'repos?per_page=' + BotConfig.max_page_count;
    console.log(url);
    var request = require('request');
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        ghArray = constructAllGithubRepoObject(body, bot, message);
    });
    console.log("getListOfAllGithubReposInOrg executed successfully.\n");
}

// Parse the Org Repos response json and extracting Repo details out of it.
function constructAllGithubRepoObject(body, bot, message) {
    console.log("Parsing the Org Repos response json and extracting Repo details out of it...");
    var orgGithubRepo = new Array();
    var obj = JSON.parse(body);
    var objLength = obj.length;
    for (var i = 0; i < objLength; i++) {
        orgGithubRepo.push(obj[i].name);
        githubGetPullRequest(obj[i].name, bot, message, false);
    }
    console.log("constructAllGithubRepoObject executed successfully.\n");
}

<commit_msg>Test commit @ 1463478440<commit_after>/**
 * GitBit Bot for Slack !
 * @author: Vasu Jain
 */

// Libraries
var https = require('https');
var BotConfig = require('./config.json');

var authTokenEncrypted = BotConfig.auth_token;
//var authTokenDecrypted = "token " + Buffer.from(authTokenEncrypted, 'base64').toString("ascii");

// For Node.js v5.11.1 and below
var buf = new Buffer(authTokenEncrypted, 'base64');
var authTokenDecrypted = "token " + buf.toString("ascii");

function onInstallation(bot, installer) {
    if (installer) {
        bot.startPrivateConversation({
            user: installer
        }, function(err, convo) {
            if (err) {
                console.log(err);
            } else {
                convo.say('I am GitBit 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
    };
}

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

/**
 * 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 !
controller.on('bot_channel_join', function(bot, message) {
    bot.reply(message, "Thank you for inviting me to your Slack Channel!")
});

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

controller.hears('pr (.*)', ['direct_mention', 'mention', 'direct_message'], function(bot, message) {
    var repo = message.match[1];
    if (typeof repo !== 'undefined' && repo) {
        var githubRepo = BotConfig.repos[repo];
        var flagZeroPRComment = false;
        if (githubRepo) {
            flagZeroPRComment = true;
            githubGetPullRequest(githubRepo, bot, message, flagZeroPRComment);
        } else if (repo == 'custom') {
            for (var r in BotConfig.repos) {
                githubGetPullRequest(BotConfig.repos[r], bot, message, flagZeroPRComment);
            }
        } else if (repo == 'all') {
            getListOfAllGithubReposInOrg(bot, message);
        } else {
            bot.reply(message, "Invalid Repo or Repo not configured");
        }
    } else {
        bot.reply(message, "Repo is undefined -- Invalid request or Repo not configured");
    }
});

// Make a POST call to GITHUB API to fetch all OPEN PR's
function githubGetPullRequest(repo, bot, message, flagZeroPRComment) {
    console.log("Making a POST call to GITHUB API to fetch all OPEN PR's...");
    var request = require('request');
    var url = BotConfig.github_api_url + 'repos/' + BotConfig.repo_org + repo + '/pulls?state=open';
    console.log(url);
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        parseAndResponse(body, bot, message, repo, flagZeroPRComment);
    });
}

// Parse the pull response json and extract PR#, Title, User out of it.
function parseAndResponse(body, bot, message, repo, flagZeroPRComment) {
    console.log("Parsing the pull response json and extracting PR#, Title, User out of it...");
    var repoSource = BotConfig.repo_org + repo + " Open Pull Requests : ";
    var response = "";
    var obj = JSON.parse(body);
    var objLength = obj.length;
    if (objLength == 0) {
        if (!BotConfig.disable_zero_pr_repo) { //if false, then only display Repo with Zero PR
            response = repoSource;
            if (flagZeroPRComment) {
                response += "No open PR's @ the moment ! Are you guys coding ?"
            } else {
                response += "0."
            }
            bot.reply(message, response);
        }
    } else {
        response = repoSource;
        for (var i = 0; i < objLength; i++) {
            response += "\nPR # " + obj[i].number + " - " + obj[i].title + " by " + obj[i].user.login;
            bot.reply(message, response);
        }
    }
    console.log(response);
    console.log("parseAndResponse for " + repo + " with " + objLength + " PR'(s) executed successfully.");
}

// Getting list of all Github Repos in an Org. Can be 100+. For the initial phase only top 100 results will display
function getListOfAllGithubReposInOrg(bot, message) {
    console.log("Getting list of all Github Repos in an Org. Can be 100+....");
    var ghArray = new Array();
    var url = BotConfig.github_api_url + 'orgs/' + BotConfig.repo_org + 'repos?per_page=' + BotConfig.max_page_count;
    console.log(url);
    var request = require('request');
    request({
        headers: {
            'Accept': 'application/vnd.github.v3+json',
            'Authorization': authTokenDecrypted,
            'User-Agent': 'GitBit-slackbot'
        },
        uri: url,
        method: 'GET'
    }, function(err, res, body) {
        ghArray = constructAllGithubRepoObject(body, bot, message);
    });
    console.log("getListOfAllGithubReposInOrg executed successfully.\n");
}

// Parse the Org Repos response json and extracting Repo details out of it.
function constructAllGithubRepoObject(body, bot, message) {
    console.log("Parsing the Org Repos response json and extracting Repo details out of it...");
    var orgGithubRepo = new Array();
    var obj = JSON.parse(body);
    var objLength = obj.length;
    for (var i = 0; i < objLength; i++) {
        orgGithubRepo.push(obj[i].name);
        githubGetPullRequest(obj[i].name, bot, message, false);
    }
    console.log("constructAllGithubRepoObject executed successfully.\n");
}

<|endoftext|>