<commit_before>/*
	Command parser for Pokemon Showdown Bot
*/

const dynCommandsDataFile = './data/commands.json';
const MAX_COMMAND_RECURSION = 10;
const MAX_CMD_FLOOD = 30;
const FLOOD_INTERVAL = 45 * 1000;

var commands = exports.commands = {};
var dynCommands = exports.dynCommands = {};
var tempVar = exports.tempVar = '';

/* Resource Monitor */

var resourceMonitor = exports.resourceMonitor = {
	cmdusage: {},
	cmdtimes: {},
	lockedlist: {},
	countcmd: function (user) {
		user = toId(user);
		var now = Date.now();
		if (!this.cmdtimes[user]) {
			this.cmdusage[user] = 1;
			this.cmdtimes[user] = now;
			return;
		}
		var duration = now - this.cmdtimes[user];
		if (user in this.cmdusage && duration < FLOOD_INTERVAL) {
			this.cmdusage[user]++;
			if (this.cmdusage[user] < MAX_CMD_FLOOD && this.cmdusage[user] % 10 === 0 && duration < 12 * 1000) {
				monitor('User ' + user + ' has used ' + this.cmdusage[user] + ' commands in the last ' + duration.duration());
			} else if (this.cmdusage[user] < MAX_CMD_FLOOD && this.cmdusage[user] % 20 === 0 && duration < 24 * 1000) {
				monitor('User ' + user + ' has used ' + this.cmdusage[user] + ' commands in the last ' + duration.duration());
			} else if (this.cmdusage[user] >= MAX_CMD_FLOOD) {
				this.lock(user);
				monitor('User ' + user + ' has been ignored (command flood: ' + this.cmdusage[user] + ' commands in the last ' + duration.duration() + ')');
				return true;
			}
		} else {
			this.cmdusage[user] = 1;
			this.cmdtimes[user] = now;
		}
	},
	lock: function (user) {
		user = toId(user);
		this.lockedlist[user] = 1;
	},
	unlock: function (user) {
		user = toId(user);
		if (this.lockedlist[user]) delete this.lockedlist[user];
	},
	isLocked: function (user) {
		return this.lockedlist[toId(user)];
	}
};

/* Information for new Users */

const REQUIRED_TIME_DIFF = 2 * 60 * 1000;

var helpMonitor = exports.helpMonitor = {
	lastHelpUsage: {},
	canUseHelpCommand: function (user) {
		if (!this.lastHelpUsage[user]) return true;
		var diff = Date.now() - this.lastHelpUsage[user];
		if (diff >= REQUIRED_TIME_DIFF) return true;
		return false;
	},
	sweepUsage: function () {
		for (var i in this.lastHelpUsage) {
			if (this.canUseHelpCommand(i)) delete this.lastHelpUsage[i];
		}
	},
	checkMessage: function (msg, words) {
		for (var i = 0; i < words.length; i++) {
			if (msg.indexOf(words[i]) >= 0) return true;
		}
		return false;
	},
	getCommand: function (by, msg) {
		var user = toId(by);
		if (!this.canUseHelpCommand(user) && !Tools.equalOrHigherRank(by, true)) return false;
		var cmd;
		if (this.checkMessage(msg.toLowerCase(), ['no', 'puedo', 'poder', 'deja']) && this.checkMessage(msg.toLowerCase(), ['hablar', 'chat', 'escribir'])) {
			cmd = 'modchat';
		} else {
			cmd = 'hola';
		}
		this.lastHelpUsage[user] = Date.now();
		this.sweepUsage();
		return cmd;
	}
};

/* Load commands */
var loadCommands = exports.loadCommands = function (reloading) {
	var errs = [];
	fs.readdirSync('./commands').forEach(function (file) {
		if (file.substr(-3) === '.js') {
			if (reloading) Tools.uncacheTree('./commands/' + file);
			try {
				Object.merge(commands, require('./commands/' + file).commands);
			} catch (e) {
				errlog(e.stack);
				error("Could not import commands file: ./commands/" + file + " | " + sys.inspect(e));
				errs.push(file);
			}
		}
	});
	if (reloading) info('Commands reloaded' + (errs.length ? ('. Errors: ' + errs.join(', ')) : ''));
	else ok('Loaded commands' + (errs.length ? ('. Errors: ' + errs.join(', ')) : ''));
	return errs;
};

if (!fs.existsSync(dynCommandsDataFile))
	fs.writeFileSync(dynCommandsDataFile, '{}');

try {
	dynCommands = exports.dynCommands = JSON.parse(fs.readFileSync(dynCommandsDataFile).toString());
} catch (e) {
	errlog(e.stack);
	error("Could not import dynamic commands: " + sys.inspect(e));
}

var writing = exports.writing = false;
var writePending = exports.writePending = false;
var saveDynCmds = exports.saveDinCmds =  function () {
	var data = JSON.stringify(dynCommands);
	var finishWriting = function () {
		writing = false;
		if (writePending) {
			writePending = false;
			saveDynCmds();
		}
	};
	if (writing) {
		writePending = true;
		return;
	}
	fs.writeFile(dynCommandsDataFile + '.0', data, function () {
		// rename is atomic on POSIX, but will throw an error on Windows
		fs.rename(dynCommandsDataFile + '.0', dynCommandsDataFile, function (err) {
			if (err) {
				// This should only happen on Windows.
				fs.writeFile(dynCommandsDataFile, data, finishWriting);
				return;
			}
			finishWriting();
		});
	});
};

/* Parser */

var commandTokens = exports.commandTokens = [];

var reloadTokens = exports.reloadTokens = function () {
	commandTokens = [];
	if (Config.commandTokens && Config.commandTokens.length) {
		for (var i = 0; i < Config.commandTokens.length; i++)
			commandTokens.push(Config.commandTokens[i]);
	}
	if (typeof Config.commandChar === "string") commandTokens.push(Config.commandChar);
	if (!commandTokens.length) {
		error('No command Tokens, using "." by default');
		commandTokens = ['.'];
	}
};

reloadTokens();

var Context = exports.Context = (function () {
	function Context (opts) {
		this.arg = opts.arg || '';
		this.by = opts.by || '';
		this.room = opts.room || 'lobby';
		this.cmd = opts.cmd;
		this.handler = opts.handler || '';
		this.cmdToken = opts.cmdToken || '';
		this.roomType = (this.room.charAt(0) === ',') ? 'pm' : (Bot.rooms[this.room] ? Bot.rooms[this.room].type : 'chat');
		this.botName = Bot.status.nickName;
		this.isExcepted = Tools.equalOrHigherRank(this.by, true);
		var lang = Config.language || 'english';
		if (this.roomType === 'chat' && Settings.settings['language'] && Settings.settings['language'][this.room]) lang = Settings.settings['language'][this.room];
		this.language = lang;
	}

	Context.prototype.reply = function (data) {
		Bot.say(this.room, data);
	};
	Context.prototype.pmReply = function (data) {
		Bot.pm(this.by, data);
	};
	Context.prototype.restrictReply = function (data, perm) {
		if (!this.can(perm)) {
			this.pmReply(data);
		} else {
			this.reply(data);
		}
	};
	Context.prototype.say = function (targetRoom, data) {
		Bot.say(targetRoom, data);
	};
	Context.prototype.isRanked = function (rank) {
		return Tools.equalOrHigherRank(this.by, rank);
	};
	Context.prototype.isRoomRanked = function (targetRoom, rank) {
		if (Bot.rooms && Bot.rooms[targetRoom] && Bot.rooms[targetRoom].users) {
			var userIdent = Bot.rooms[targetRoom].users[toId(this.by)] || this.by;
			return Tools.equalOrHigherRank(userIdent, rank);
		}
		return this.isRanked(rank);
	};
	Context.prototype.can = function (permission) {
		if (this.roomType === 'battle') return Settings.userCan('battle-', this.by, permission);
		else return Settings.userCan(this.room, this.by, permission);
	};
	Context.prototype.canSet = function (permission, rank) {
		var rankSet;
		if (!Settings.settings['commands'] || !Settings.settings['commands'][this.room] || typeof Settings.settings['commands'][this.room][permission] === "undefined") {
			rankSet = Config.defaultPermission;
			if (Config.permissionExceptions[permission]) rankSet = Config.permissionExceptions[permission];
		} else {
			rankSet = Settings.settings['commands'][this.room][permission];
		}
		if (Tools.equalOrHigherRank(this.by, rankSet) && Tools.equalOrHigherRank(this.by, rank)) return true;
		return false;
	};
	Context.prototype.botRanked = function (rank) {
		if (!Bot.rooms[this.room]) return false;
		var ident = Bot.rooms[this.room].users[toId(Bot.status.nickName)];
		if (ident) return Tools.equalOrHigherRank(ident, rank);
		return false;
	};
	Context.prototype.hasRank = function (user, rank, targetRoom) {
		if (!targetRoom) targetRoom = this.room;
		if (Bot.rooms && Bot.rooms[targetRoom] && Bot.rooms[targetRoom].users) {
			var userIdent = Bot.rooms[targetRoom].users[toId(user)] || user;
			return Tools.equalOrHigherRank(userIdent, rank);
		}
		return Tools.equalOrHigherRank(user, rank);
	};
	Context.prototype.getRoom = function (targetRoom) {
		if (!Bot.rooms[targetRoom]) return null;
		var roomObj = {};
		roomObj.id = toRoomid(targetRoom);
		roomObj.type = Bot.rooms[targetRoom].type;
		roomObj.title = Bot.rooms[targetRoom].title;
		roomObj.users = this.getRoomUsers(targetRoom);
		return roomObj;
	};
	Context.prototype.getRoomUsers = function (targetRoom) {
		if (!Bot.rooms[targetRoom]) return null;
		var users = [];
		for (var i in Bot.rooms[targetRoom].users) {
			users.push(Bot.rooms[targetRoom].users[i]);
		}
		return users;
	};
	Context.prototype.getUser = function (user, targetRoom) {
		if (!Bot.rooms[targetRoom]) return null;
		user = toId(user);
		if (!(user in Bot.rooms[targetRoom].users)) return null;
		return {
			room: targetRoom,
			ident: Bot.rooms[targetRoom].users[user],
			id: toId(Bot.rooms[targetRoom].users[user]),
			name: Bot.rooms[targetRoom].users[user].substr(1),
			rank: Bot.rooms[targetRoom].users[user].charAt(0)
		};
	};
	Context.prototype.trad = Context.prototype.tra = function (data) {
		return Tools.translateCmd(this.handler, data, this.language);
	};
	Context.prototype.parse = function (data) {
		return exports.parse(this.room, this.by, data);
	};

	return Context;
})();

var parse = exports.parse = function (room, by, msg) {
	if (!Tools.equalOrHigherRank(by, true)) {
		if (resourceMonitor.isLocked(by)) return;
	}
	if (msg.substr(0, 8) === '/invite ' && Tools.equalOrHigherRank(by, '%')) {
		Bot.say('', '/join ' +  msg.substr(8));
		return;
	}

	var cmdToken = null;

	for (var i = 0; i < commandTokens.length; i++) {
		if (typeof commandTokens[i] === "string" && msg.substr(0, commandTokens[i].length) === commandTokens[i]) {
			cmdToken = commandTokens[i];
			break;
		}
	}

	if (!cmdToken) {
		if (room.charAt(0) === ',') {
			var pseudoCmd = helpMonitor.getCommand(by, msg);
			if (pseudoCmd) parse(room, by, commandTokens[0] + pseudoCmd);
		} else if (msg.substr(0, 3) === '/me') {
			parse(room, by, commandTokens[0] + "me" + msg.substr(3));
		}
		return;
	}

	var toParse = msg.substr(cmdToken.length);
	var spaceIndex = toParse.indexOf(' ');

	var cmd, args;
	if (spaceIndex === -1) {
		cmd = toParse;
		args = '';
	} else {
		cmd = toParse.substr(0, spaceIndex);
		args = toParse.substr(spaceIndex + 1);
	}

	cmd = cmd.toLowerCase();

	if (!commands[cmd] && dynCommands[toId(cmd)]) {
		args = cmd;
		cmd = 'dyn';
	}

	if (commands[cmd]) {
		var handler = cmd;
		var loopBreaker = 0;
		while (typeof commands[handler] === 'string' && (loopBreaker++ < MAX_COMMAND_RECURSION)) {
			handler = commands[handler];
		}
		if (typeof commands[handler] === 'function') {
			var opts = {
				arg: args,
				by: by,
				room: room,
				cmd: cmd,
				handler: handler,
				cmdToken: cmdToken
			};
			var context = new Context(opts);
			try {
				if (!Tools.equalOrHigherRank(by, true)) {
					if (resourceMonitor.countcmd(by)) return;
				}
				cmdr(handler + ' | arg: ' + args + ' | by: ' + by + ' | room: ' + room + ' | cmd: ' + cmd);
				commands[handler].call(context, args, by, room, cmd);
			} catch (e) {
				errlog(e.stack);
				error("Command crash: " + cmd + ' | by: ' + by + ' | room: ' + room + ' | ' + sys.inspect(e));
				Bot.say(room, 'The command crashed: ' + sys.inspect(e).toString().split('\n').join(' '));
			}
		} else {
			error("unkwown command type: " + cmd + ' = ' + sys.inspect(commands[handler]));
		}
	}
};
<commit_msg>Restrict /invite<commit_after>/*
	Command parser for Pokemon Showdown Bot
*/

const dynCommandsDataFile = './data/commands.json';
const MAX_COMMAND_RECURSION = 10;
const MAX_CMD_FLOOD = 30;
const FLOOD_INTERVAL = 45 * 1000;

var commands = exports.commands = {};
var dynCommands = exports.dynCommands = {};
var tempVar = exports.tempVar = '';

/* Resource Monitor */

var resourceMonitor = exports.resourceMonitor = {
	cmdusage: {},
	cmdtimes: {},
	lockedlist: {},
	countcmd: function (user) {
		user = toId(user);
		var now = Date.now();
		if (!this.cmdtimes[user]) {
			this.cmdusage[user] = 1;
			this.cmdtimes[user] = now;
			return;
		}
		var duration = now - this.cmdtimes[user];
		if (user in this.cmdusage && duration < FLOOD_INTERVAL) {
			this.cmdusage[user]++;
			if (this.cmdusage[user] < MAX_CMD_FLOOD && this.cmdusage[user] % 10 === 0 && duration < 12 * 1000) {
				monitor('User ' + user + ' has used ' + this.cmdusage[user] + ' commands in the last ' + duration.duration());
			} else if (this.cmdusage[user] < MAX_CMD_FLOOD && this.cmdusage[user] % 20 === 0 && duration < 24 * 1000) {
				monitor('User ' + user + ' has used ' + this.cmdusage[user] + ' commands in the last ' + duration.duration());
			} else if (this.cmdusage[user] >= MAX_CMD_FLOOD) {
				this.lock(user);
				monitor('User ' + user + ' has been ignored (command flood: ' + this.cmdusage[user] + ' commands in the last ' + duration.duration() + ')');
				return true;
			}
		} else {
			this.cmdusage[user] = 1;
			this.cmdtimes[user] = now;
		}
	},
	lock: function (user) {
		user = toId(user);
		this.lockedlist[user] = 1;
	},
	unlock: function (user) {
		user = toId(user);
		if (this.lockedlist[user]) delete this.lockedlist[user];
	},
	isLocked: function (user) {
		return this.lockedlist[toId(user)];
	}
};

/* Information for new Users */

const REQUIRED_TIME_DIFF = 2 * 60 * 1000;

var helpMonitor = exports.helpMonitor = {
	lastHelpUsage: {},
	canUseHelpCommand: function (user) {
		if (!this.lastHelpUsage[user]) return true;
		var diff = Date.now() - this.lastHelpUsage[user];
		if (diff >= REQUIRED_TIME_DIFF) return true;
		return false;
	},
	sweepUsage: function () {
		for (var i in this.lastHelpUsage) {
			if (this.canUseHelpCommand(i)) delete this.lastHelpUsage[i];
		}
	},
	checkMessage: function (msg, words) {
		for (var i = 0; i < words.length; i++) {
			if (msg.indexOf(words[i]) >= 0) return true;
		}
		return false;
	},
	getCommand: function (by, msg) {
		var user = toId(by);
		if (!this.canUseHelpCommand(user) && !Tools.equalOrHigherRank(by, true)) return false;
		var cmd;
		if (this.checkMessage(msg.toLowerCase(), ['no', 'puedo', 'poder', 'deja']) && this.checkMessage(msg.toLowerCase(), ['hablar', 'chat', 'escribir'])) {
			cmd = 'modchat';
		} else {
			cmd = 'hola';
		}
		this.lastHelpUsage[user] = Date.now();
		this.sweepUsage();
		return cmd;
	}
};

/* Load commands */
var loadCommands = exports.loadCommands = function (reloading) {
	var errs = [];
	fs.readdirSync('./commands').forEach(function (file) {
		if (file.substr(-3) === '.js') {
			if (reloading) Tools.uncacheTree('./commands/' + file);
			try {
				Object.merge(commands, require('./commands/' + file).commands);
			} catch (e) {
				errlog(e.stack);
				error("Could not import commands file: ./commands/" + file + " | " + sys.inspect(e));
				errs.push(file);
			}
		}
	});
	if (reloading) info('Commands reloaded' + (errs.length ? ('. Errors: ' + errs.join(', ')) : ''));
	else ok('Loaded commands' + (errs.length ? ('. Errors: ' + errs.join(', ')) : ''));
	return errs;
};

if (!fs.existsSync(dynCommandsDataFile))
	fs.writeFileSync(dynCommandsDataFile, '{}');

try {
	dynCommands = exports.dynCommands = JSON.parse(fs.readFileSync(dynCommandsDataFile).toString());
} catch (e) {
	errlog(e.stack);
	error("Could not import dynamic commands: " + sys.inspect(e));
}

var writing = exports.writing = false;
var writePending = exports.writePending = false;
var saveDynCmds = exports.saveDinCmds =  function () {
	var data = JSON.stringify(dynCommands);
	var finishWriting = function () {
		writing = false;
		if (writePending) {
			writePending = false;
			saveDynCmds();
		}
	};
	if (writing) {
		writePending = true;
		return;
	}
	fs.writeFile(dynCommandsDataFile + '.0', data, function () {
		// rename is atomic on POSIX, but will throw an error on Windows
		fs.rename(dynCommandsDataFile + '.0', dynCommandsDataFile, function (err) {
			if (err) {
				// This should only happen on Windows.
				fs.writeFile(dynCommandsDataFile, data, finishWriting);
				return;
			}
			finishWriting();
		});
	});
};

/* Parser */

var commandTokens = exports.commandTokens = [];

var reloadTokens = exports.reloadTokens = function () {
	commandTokens = [];
	if (Config.commandTokens && Config.commandTokens.length) {
		for (var i = 0; i < Config.commandTokens.length; i++)
			commandTokens.push(Config.commandTokens[i]);
	}
	if (typeof Config.commandChar === "string") commandTokens.push(Config.commandChar);
	if (!commandTokens.length) {
		error('No command Tokens, using "." by default');
		commandTokens = ['.'];
	}
};

reloadTokens();

var Context = exports.Context = (function () {
	function Context (opts) {
		this.arg = opts.arg || '';
		this.by = opts.by || '';
		this.room = opts.room || 'lobby';
		this.cmd = opts.cmd;
		this.handler = opts.handler || '';
		this.cmdToken = opts.cmdToken || '';
		this.roomType = (this.room.charAt(0) === ',') ? 'pm' : (Bot.rooms[this.room] ? Bot.rooms[this.room].type : 'chat');
		this.botName = Bot.status.nickName;
		this.isExcepted = Tools.equalOrHigherRank(this.by, true);
		var lang = Config.language || 'english';
		if (this.roomType === 'chat' && Settings.settings['language'] && Settings.settings['language'][this.room]) lang = Settings.settings['language'][this.room];
		this.language = lang;
	}

	Context.prototype.reply = function (data) {
		Bot.say(this.room, data);
	};
	Context.prototype.pmReply = function (data) {
		Bot.pm(this.by, data);
	};
	Context.prototype.restrictReply = function (data, perm) {
		if (!this.can(perm)) {
			this.pmReply(data);
		} else {
			this.reply(data);
		}
	};
	Context.prototype.say = function (targetRoom, data) {
		Bot.say(targetRoom, data);
	};
	Context.prototype.isRanked = function (rank) {
		return Tools.equalOrHigherRank(this.by, rank);
	};
	Context.prototype.isRoomRanked = function (targetRoom, rank) {
		if (Bot.rooms && Bot.rooms[targetRoom] && Bot.rooms[targetRoom].users) {
			var userIdent = Bot.rooms[targetRoom].users[toId(this.by)] || this.by;
			return Tools.equalOrHigherRank(userIdent, rank);
		}
		return this.isRanked(rank);
	};
	Context.prototype.can = function (permission) {
		if (this.roomType === 'battle') return Settings.userCan('battle-', this.by, permission);
		else return Settings.userCan(this.room, this.by, permission);
	};
	Context.prototype.canSet = function (permission, rank) {
		var rankSet;
		if (!Settings.settings['commands'] || !Settings.settings['commands'][this.room] || typeof Settings.settings['commands'][this.room][permission] === "undefined") {
			rankSet = Config.defaultPermission;
			if (Config.permissionExceptions[permission]) rankSet = Config.permissionExceptions[permission];
		} else {
			rankSet = Settings.settings['commands'][this.room][permission];
		}
		if (Tools.equalOrHigherRank(this.by, rankSet) && Tools.equalOrHigherRank(this.by, rank)) return true;
		return false;
	};
	Context.prototype.botRanked = function (rank) {
		if (!Bot.rooms[this.room]) return false;
		var ident = Bot.rooms[this.room].users[toId(Bot.status.nickName)];
		if (ident) return Tools.equalOrHigherRank(ident, rank);
		return false;
	};
	Context.prototype.hasRank = function (user, rank, targetRoom) {
		if (!targetRoom) targetRoom = this.room;
		if (Bot.rooms && Bot.rooms[targetRoom] && Bot.rooms[targetRoom].users) {
			var userIdent = Bot.rooms[targetRoom].users[toId(user)] || user;
			return Tools.equalOrHigherRank(userIdent, rank);
		}
		return Tools.equalOrHigherRank(user, rank);
	};
	Context.prototype.getRoom = function (targetRoom) {
		if (!Bot.rooms[targetRoom]) return null;
		var roomObj = {};
		roomObj.id = toRoomid(targetRoom);
		roomObj.type = Bot.rooms[targetRoom].type;
		roomObj.title = Bot.rooms[targetRoom].title;
		roomObj.users = this.getRoomUsers(targetRoom);
		return roomObj;
	};
	Context.prototype.getRoomUsers = function (targetRoom) {
		if (!Bot.rooms[targetRoom]) return null;
		var users = [];
		for (var i in Bot.rooms[targetRoom].users) {
			users.push(Bot.rooms[targetRoom].users[i]);
		}
		return users;
	};
	Context.prototype.getUser = function (user, targetRoom) {
		if (!Bot.rooms[targetRoom]) return null;
		user = toId(user);
		if (!(user in Bot.rooms[targetRoom].users)) return null;
		return {
			room: targetRoom,
			ident: Bot.rooms[targetRoom].users[user],
			id: toId(Bot.rooms[targetRoom].users[user]),
			name: Bot.rooms[targetRoom].users[user].substr(1),
			rank: Bot.rooms[targetRoom].users[user].charAt(0)
		};
	};
	Context.prototype.trad = Context.prototype.tra = function (data) {
		return Tools.translateCmd(this.handler, data, this.language);
	};
	Context.prototype.parse = function (data) {
		return exports.parse(this.room, this.by, data);
	};

	return Context;
})();

var parse = exports.parse = function (room, by, msg) {
	if (!Tools.equalOrHigherRank(by, true)) {
		if (resourceMonitor.isLocked(by)) return;
	}
	if (msg.substr(0, 8) === '/invite ' && Tools.equalOrHigherRank(by, true)) {
		Bot.say('', '/join ' +  msg.substr(8));
		return;
	}

	var cmdToken = null;

	for (var i = 0; i < commandTokens.length; i++) {
		if (typeof commandTokens[i] === "string" && msg.substr(0, commandTokens[i].length) === commandTokens[i]) {
			cmdToken = commandTokens[i];
			break;
		}
	}

	if (!cmdToken) {
		if (room.charAt(0) === ',') {
			var pseudoCmd = helpMonitor.getCommand(by, msg);
			if (pseudoCmd) parse(room, by, commandTokens[0] + pseudoCmd);
		} else if (msg.substr(0, 3) === '/me') {
			parse(room, by, commandTokens[0] + "me" + msg.substr(3));
		}
		return;
	}

	var toParse = msg.substr(cmdToken.length);
	var spaceIndex = toParse.indexOf(' ');

	var cmd, args;
	if (spaceIndex === -1) {
		cmd = toParse;
		args = '';
	} else {
		cmd = toParse.substr(0, spaceIndex);
		args = toParse.substr(spaceIndex + 1);
	}

	cmd = cmd.toLowerCase();

	if (!commands[cmd] && dynCommands[toId(cmd)]) {
		args = cmd;
		cmd = 'dyn';
	}

	if (commands[cmd]) {
		var handler = cmd;
		var loopBreaker = 0;
		while (typeof commands[handler] === 'string' && (loopBreaker++ < MAX_COMMAND_RECURSION)) {
			handler = commands[handler];
		}
		if (typeof commands[handler] === 'function') {
			var opts = {
				arg: args,
				by: by,
				room: room,
				cmd: cmd,
				handler: handler,
				cmdToken: cmdToken
			};
			var context = new Context(opts);
			try {
				if (!Tools.equalOrHigherRank(by, true)) {
					if (resourceMonitor.countcmd(by)) return;
				}
				cmdr(handler + ' | arg: ' + args + ' | by: ' + by + ' | room: ' + room + ' | cmd: ' + cmd);
				commands[handler].call(context, args, by, room, cmd);
			} catch (e) {
				errlog(e.stack);
				error("Command crash: " + cmd + ' | by: ' + by + ' | room: ' + room + ' | ' + sys.inspect(e));
				Bot.say(room, 'The command crashed: ' + sys.inspect(e).toString().split('\n').join(' '));
			}
		} else {
			error("unkwown command type: " + cmd + ' = ' + sys.inspect(commands[handler]));
		}
	}
};
<|endoftext|>


<commit_before>// Some common functionality shared amongst record types.

'use strict';

var bufferUtils = require('../buffer_utils');

function getPairLengths(pair) {
  return pair.map(function (element) {
    return Buffer.byteLength(element);
  });
}

exports.calculateNameValuePairsLength = function(pairs) {
  var size = 0;

  pairs.forEach(function(pair) {
    if(!Array.isArray(pair)) {
      pair = [pair, ''];
    }

    var lengths = getPairLengths(pair);
    size += lengths[0];
    size += lengths[1];
    size += (lengths[0] > 127) ? 4 : 1;
    size += (lengths[1] > 127) ? 4 : 1;
  });

  return size;
};

exports.writeNameValuePairs = function(buffer, pairs) {
  var index = 0;
  pairs.forEach(function(pair) {
    if(!Array.isArray(pair)) {
      pair = [pair, ''];
    }

    var lengths = getPairLengths(pair);

    if(lengths[0] > 127) {
      bufferUtils.setInt32(buffer, index, lengths[0]+ 2147483648);
      index += 4;
    }
    else {
      buffer[index++] = lengths[0];
    }

    if(lengths[1]> 127) {
      bufferUtils.setInt32(buffer, index, lengths[1]+ 2147483648);
      index += 4;
    }
    else {
      buffer[index++] = lengths[1];
    }

    buffer.write(pair[0], index);
    index += lengths[0];
    buffer.write(pair[1], index);
    index += lengths[1];
  });
};

function readNameValuePair(buffer, offset) {

}

exports.readNameValuePairs = function(buffer) {
  var offset = 0, len = buffer.length, pairs = [], keyLength, valueLength, key, value;

  while(offset < len) {
    keyLength = buffer[offset++];
    if(keyLength & 0x80) {
      keyLength = ((keyLength - 128) << 24) | (buffer[offset++] << 16) | (buffer[offset++] << 8) | buffer[offset++];
    }

    valueLength = buffer[offset++];
    if(valueLength & 0x80) {
      valueLength = ((valueLength - 128) << 24) | (buffer[offset++] << 16) | (buffer[offset++] << 8) | buffer[offset++];
    }

    key = buffer.toString('utf8', offset, offset + keyLength);
    offset += keyLength;
    if(valueLength) {
      value = buffer.toString('utf8', offset, offset + valueLength);
      offset += valueLength;
    }

    pairs.push(valueLength ? [key, value] : key);
  }

  return pairs;
};
<commit_msg>More work on simplifying lib/records/common.js<commit_after>// Some common functionality shared amongst record types.

/* jshint bitwise: true */

'use strict';

var bufferUtils = require('../buffer_utils');

function getPairLengths(pair) {
  return pair.map(function (element) {
    return Buffer.byteLength(element);
  });
}

exports.calculateNameValuePairsLength = function(pairs) {
  var size = 0;

  pairs.forEach(function(pair) {
    if(!Array.isArray(pair)) {
      pair = [pair, ''];
    }

    var lengths = getPairLengths(pair);
    size += lengths[0];
    size += lengths[1];
    size += (lengths[0] > 127) ? 4 : 1;
    size += (lengths[1] > 127) ? 4 : 1;
  });

  return size;
};

exports.writeNameValuePairs = function(buffer, pairs) {
  var index = 0;
  pairs.forEach(function(pair) {
    if(!Array.isArray(pair)) {
      pair = [pair, ''];
    }

    var lengths = getPairLengths(pair);

    if(lengths[0] > 127) {
      bufferUtils.setInt32(buffer, index, lengths[0]+ 2147483648);
      index += 4;
    }
    else {
      buffer[index++] = lengths[0];
    }

    if(lengths[1]> 127) {
      bufferUtils.setInt32(buffer, index, lengths[1]+ 2147483648);
      index += 4;
    }
    else {
      buffer[index++] = lengths[1];
    }

    buffer.write(pair[0], index);
    index += lengths[0];
    buffer.write(pair[1], index);
    index += lengths[1];
  });
};

function readLength(buffer, offset) {
  var length = buffer[offset++];
  if(length & 0x80) {
    length = ((length - 128) << 24) | (buffer[offset++] << 16) | (buffer[offset++] << 8) | buffer[offset++];
  }
  return [length, offset];
}

exports.readNameValuePairs = function(buffer) {
  var offset = 0, len = buffer.length, pairs = [], keyLength, valueLength, key, value;

  while(offset < len) {
    var result = readLength(buffer, offset);
    keyLength = result[0];
    offset = result[1];

    result = readLength(buffer, offset);
    valueLength = result[0];
    offset = result[1];

    key = buffer.toString('utf8', offset, offset + keyLength);
    offset += keyLength;
    if(valueLength) {
      value = buffer.toString('utf8', offset, offset + valueLength);
      offset += valueLength;
    }

    pairs.push(valueLength ? [key, value] : key);
  }

  return pairs;
};
<|endoftext|>


<commit_before>/* eslint no-unused-vars:0 */
import React, { Component, PropTypes } from 'react';
import { build, getNext, getPrev } from '../engines/carousel';
import { isInsideLeft, isInsideRight } from '../engines/bounds';
import { calculateElSpace } from '../engines/helpers';
import { addListener, removeListener, userConfig } from '../listener';
import { isBlocked, block } from '../clock';
import { isActive } from '../isActive';
import { execCb } from '../funcHandler';
import { addCarouselToStore, _updateBinder } from '../redux/actions';
import { CAROUSEL_TYPE, NAVIGATION_BOUND, NAVIGATION_CENTER } from '../constants';
import { hasDiff } from '../engines/helpers';

class Carousel extends Component {

  static get propTypes() {
    return {
      children: React.PropTypes.oneOfType([
        React.PropTypes.object,
        React.PropTypes.array,
      ]),
      id: PropTypes.string.isRequired,
      active: PropTypes.bool,
      index: PropTypes.number,
      size: PropTypes.number,
      speed: PropTypes.number,
      debounce: PropTypes.number,
      elWidth: PropTypes.number,
      navigation: PropTypes.string,
      circular: PropTypes.bool,
      gap: PropTypes.number,
      className: PropTypes.string,
      childrenClassName: PropTypes.string,
      onChange: PropTypes.func,
      onDownExit: PropTypes.func,
      onUpExit: PropTypes.func,
      onEnter: PropTypes.func,
      updateIndex: PropTypes.bool
    };
  }

  static get defaultProps() {
    return {
      index: 0,
      size: 3,
      elWidth: 100,
      circular: true,
      active: true,
      speed: 100,
      gap: 0,
      navigation: NAVIGATION_CENTER,
      debounce: 82,
      className: 'carousel',
      childrenClassName: 'carousel-child',
      updateIndex: false
    };
  }

  constructor(props) {
    super(props);
    this.listenerId = addListener(this.keysHandler, this);
    this.timeout = null;
    this.movingCountDown = () => this.timeout = setTimeout(() =>
      _updateBinder(props.id, { moving: false, selectedId: this.selectedId }), props.speed);
    this.state = { cursor: props.index, elements: [] };
  }

  computeChildren(children) {
    let returnValue = children;
    if (Object.prototype.toString.call(children) !== '[object Array]') {
      returnValue = [children];
    }
    let inc = 1;
    while (returnValue.length <= this.props.size + 4) {
      const addedValues = returnValue.map(child => {
        const props = {
          ...child.props,
          id: child.props.id + '_' + inc,
        };
        return { ...child, props: props, key: child.props.id + '_' + inc };
      });
      returnValue = returnValue.concat(addedValues);
      inc++;
    }
    return returnValue;
  }

  keysHandler(keyCode) {
    const { children, circular, onDownExit, onUpExit, onEnter } = this.props;
    const { cursor } = this.state;
    if (isActive(this.props) && !isBlocked()) {
      switch (keyCode) {
        case userConfig.left:
          if (!circular && cursor === 0) return;
          this.performAction(getPrev(children.length, cursor));
          break;
        case userConfig.right:
          if (!circular && cursor === children.length - 1) return;
          this.performAction(getNext(children.length, cursor));
          break;
        case userConfig.down:
          this.performCallback(onDownExit);
          break;
        case userConfig.up:
          this.performCallback(onUpExit);
          break;
        case userConfig.enter:
          this.performCallback(onEnter);
          break;
      }
    }
  }

  isLeftMove(currentCursor, nextCursor, children) {
    return (nextCursor < currentCursor)
      || (currentCursor === 0 && nextCursor === children.length - 1);
  }

  componentWillMount() {
    addCarouselToStore(this.props, CAROUSEL_TYPE);
    this.updateState(this.state.cursor, this.props.children);
  }

  componentWillUpdate({ index, children, updateIndex }) {
    if (hasDiff(children, this.props.children) || this.props.index !== index) {
      const cursor = updateIndex ? index : this.state.cursor;
      this.updateState(cursor, children);
    }
  }

  performAction(cursor) {
    const { debounce, onChange, children } = this.props;
    block(debounce);
    clearTimeout(this.timeout);
    this.updateState(cursor, children);
    this.movingCountDown();
    execCb(onChange, this.selectedId, this);
  }

  updateState(cursor, children) {
    const computedChildren = this.computeChildren(children);
    const { id, size, circular } = this.props;
    if (!computedChildren[cursor]) {
      return;
    }
    this.selectedId = computedChildren[cursor].props.id;
    _updateBinder(id, { selectedId: this.selectedId, cursor, moving: true });
    const elements = build(computedChildren, size + 4, cursor, circular);
    this.setState({
      cursor,
      elements,
      gaps: this.determineGap(elements, this.isLeftMove(this.state.cursor, cursor, elements)),
    });
  }

  performCallback(callback) {
    if (callback) {
      block();
      execCb(callback, this.selectedId, this);
    }
  }

  determineGap(elements, leftMove) {
    const { navigation, id, elWidth, size, gap } = this.props;
    const { gaps } = this.state;
    const standardGaps = gaps
      || elements.map((el, inc) => (inc - (navigation === NAVIGATION_BOUND ? size - 1 : 2)) * elWidth + gap);

    if (navigation === NAVIGATION_BOUND) {
      const selected = calculateElSpace(document.getElementById(this.selectedId));
      if (!selected) {
        return standardGaps
      }
      const wrapper = calculateElSpace(document.getElementById(id));
      if ((!leftMove && isInsideRight(wrapper, selected, gap))) {
        return standardGaps.map(stdGap => stdGap + elWidth);
      } else if (leftMove && isInsideLeft(wrapper, selected, gap)) {
        return standardGaps.map(stdGap => stdGap - elWidth);
      } else {
        return standardGaps;
      }
    }

    return standardGaps;
  }

  render() {
    const { size, elWidth, childrenClassName, className, id } = this.props;
    const { elements, gaps } = this.state;
    return

      {elements.map((element, inc) => {
        if (!element) return;
        const gap = gaps[inc];
        return <div key={element.props.id} className={childrenClassName} style={{
          marginLeft: `${gap}px`,
          position: 'absolute',
          width: `${elWidth}px`,
          display: 'block',
          opacity: (gap === -(2 * elWidth) || gap === (size + 1) * elWidth) ? 0 : 1,
        }}>{element}
;
      })}

;
  }

  componentWillUnmount() {
    removeListener(this.listenerId);
  }

}

export default Carousel;
<commit_msg>fix infinite loop issue<commit_after>/* eslint no-unused-vars:0 */
import React, { Component, PropTypes } from 'react';
import { build, getNext, getPrev } from '../engines/carousel';
import { isInsideLeft, isInsideRight } from '../engines/bounds';
import { calculateElSpace } from '../engines/helpers';
import { addListener, removeListener, userConfig } from '../listener';
import { isBlocked, block } from '../clock';
import { isActive } from '../isActive';
import { execCb } from '../funcHandler';
import { addCarouselToStore, _updateBinder } from '../redux/actions';
import { CAROUSEL_TYPE, NAVIGATION_BOUND, NAVIGATION_CENTER } from '../constants';
import { hasDiff } from '../engines/helpers';

class Carousel extends Component {

  static get propTypes() {
    return {
      children: React.PropTypes.oneOfType([
        React.PropTypes.object,
        React.PropTypes.array,
      ]),
      id: PropTypes.string.isRequired,
      active: PropTypes.bool,
      index: PropTypes.number,
      size: PropTypes.number,
      speed: PropTypes.number,
      debounce: PropTypes.number,
      elWidth: PropTypes.number,
      navigation: PropTypes.string,
      circular: PropTypes.bool,
      gap: PropTypes.number,
      className: PropTypes.string,
      childrenClassName: PropTypes.string,
      onChange: PropTypes.func,
      onDownExit: PropTypes.func,
      onUpExit: PropTypes.func,
      onEnter: PropTypes.func,
      updateIndex: PropTypes.bool
    };
  }

  static get defaultProps() {
    return {
      index: 0,
      size: 3,
      elWidth: 100,
      circular: true,
      active: true,
      speed: 100,
      gap: 0,
      navigation: NAVIGATION_CENTER,
      debounce: 82,
      className: 'carousel',
      childrenClassName: 'carousel-child',
      updateIndex: false
    };
  }

  constructor(props) {
    super(props);
    this.listenerId = addListener(this.keysHandler, this);
    this.timeout = null;
    this.movingCountDown = () => this.timeout = setTimeout(() =>
      _updateBinder(props.id, { moving: false, selectedId: this.selectedId }), props.speed);
    this.state = { cursor: props.index, elements: [] };
  }

  computeChildren(children) {
    let returnValue = children;
    if (Object.prototype.toString.call(children) !== '[object Array]') {
      returnValue = [children];
    }
    if (returnValue.length === 0) {
      return returnValue;
    }
    let inc = 1;
    while (returnValue.length <= this.props.size + 4) {
      const addedValues = returnValue.map(child => {
        const props = {
          ...child.props,
          id: child.props.id + '_' + inc,
        };
        return { ...child, props: props, key: child.props.id + '_' + inc };
      });
      returnValue = returnValue.concat(addedValues);
      inc++;
    }
    return returnValue;
  }

  keysHandler(keyCode) {
    const { children, circular, onDownExit, onUpExit, onEnter } = this.props;
    const { cursor } = this.state;
    if (isActive(this.props) && !isBlocked()) {
      switch (keyCode) {
        case userConfig.left:
          if (!circular && cursor === 0) return;
          this.performAction(getPrev(children.length, cursor));
          break;
        case userConfig.right:
          if (!circular && cursor === children.length - 1) return;
          this.performAction(getNext(children.length, cursor));
          break;
        case userConfig.down:
          this.performCallback(onDownExit);
          break;
        case userConfig.up:
          this.performCallback(onUpExit);
          break;
        case userConfig.enter:
          this.performCallback(onEnter);
          break;
      }
    }
  }

  isLeftMove(currentCursor, nextCursor, children) {
    return (nextCursor < currentCursor)
      || (currentCursor === 0 && nextCursor === children.length - 1);
  }

  componentWillMount() {
    addCarouselToStore(this.props, CAROUSEL_TYPE);
    this.updateState(this.state.cursor, this.props.children);
  }

  componentWillUpdate({ index, children, updateIndex }) {
    if (hasDiff(children, this.props.children) || this.props.index !== index) {
      const cursor = updateIndex ? index : this.state.cursor;
      this.updateState(cursor, children);
    }
  }

  performAction(cursor) {
    const { debounce, onChange, children } = this.props;
    block(debounce);
    clearTimeout(this.timeout);
    this.updateState(cursor, children);
    this.movingCountDown();
    execCb(onChange, this.selectedId, this);
  }

  updateState(cursor, children) {
    const computedChildren = this.computeChildren(children);
    const { id, size, circular } = this.props;
    if (!computedChildren[cursor]) {
      return;
    }
    this.selectedId = computedChildren[cursor].props.id;
    _updateBinder(id, { selectedId: this.selectedId, cursor, moving: true });
    const elements = build(computedChildren, size + 4, cursor, circular);
    this.setState({
      cursor,
      elements,
      gaps: this.determineGap(elements, this.isLeftMove(this.state.cursor, cursor, elements)),
    });
  }

  performCallback(callback) {
    if (callback) {
      block();
      execCb(callback, this.selectedId, this);
    }
  }

  determineGap(elements, leftMove) {
    const { navigation, id, elWidth, size, gap } = this.props;
    const { gaps } = this.state;
    const standardGaps = gaps
      || elements.map((el, inc) => (inc - (navigation === NAVIGATION_BOUND ? size - 1 : 2)) * elWidth + gap);

    if (navigation === NAVIGATION_BOUND) {
      const selected = calculateElSpace(document.getElementById(this.selectedId));
      if (!selected) {
        return standardGaps
      }
      const wrapper = calculateElSpace(document.getElementById(id));
      if ((!leftMove && isInsideRight(wrapper, selected, gap))) {
        return standardGaps.map(stdGap => stdGap + elWidth);
      } else if (leftMove && isInsideLeft(wrapper, selected, gap)) {
        return standardGaps.map(stdGap => stdGap - elWidth);
      } else {
        return standardGaps;
      }
    }

    return standardGaps;
  }

  render() {
    const { size, elWidth, childrenClassName, className, id } = this.props;
    const { elements, gaps } = this.state;
    return

      {elements.map((element, inc) => {
        if (!element) return;
        const gap = gaps[inc];
        return <div key={element.props.id} className={childrenClassName} style={{
          marginLeft: `${gap}px`,
          position: 'absolute',
          width: `${elWidth}px`,
          display: 'block',
          opacity: (gap === -(2 * elWidth) || gap === (size + 1) * elWidth) ? 0 : 1,
        }}>{element}
;
      })}

;
  }

  componentWillUnmount() {
    removeListener(this.listenerId);
  }

}

export default Carousel;
<|endoftext|>


<commit_before>var _ = require('lodash')
var random_name = require('node-random-name');
var Firebase = require('firebase');

// San Francisco
var city_location = {
  lat: 37.78,
  lon: -122.41
}

var radius = 0.03

// simualate a random person entering, staying for a duration, and leaving
function simulate(){

  // generate a random person with a random name,
  // random location, and random duration
  var name = random_name()
  var duration = 1 + 5 * Math.random()
  var lat = city_location.lat + radius * (Math.random() - 0.5) * 2
  var lon = city_location.lon + radius * (Math.random() - 0.5) * 2
  var person = {
    name: name,
    duration: duration,
    lat: lat,
    lon: lon
  }

  // simulate this person entering
  enter(person)

  // simulate this person leaving after 'duration' seconds
  setTimeout(function(){
    leave(person)
  }, duration * 1000)

}

function enter(person){
  console.log('enter', person)
  // TODO: put this person in the Firebase
  // var ref = new Firebase('your-firebase-url')
  // ...
  var ref = new Firebase('https://team-polive.firebaseio.com/')
  ref.child(person.name).set({
    name: person.name,
    lat: person.lat,
    lon: person.lon
  });
  var newTaskRef = ref.push()
}

function leave(person) {
  console.log('leave', person)
  // TODO: remove this person from the Firebase
  // var ref = new Firebase('your-firebase-url')
  // ...
  var ref = new Firebase('https://team-polive.firebaseio.com/')
  ref.child(person.name).remove();
}

function clear(){
  // TODO: remove all people from the Firebase
  // var ref = new Firebase('your-firebase-url')
  // ...
  // TODO: remove this person from the Firebase
  // var ref = new Firebase('your-firebase-url')
  var ref = new Firebase('https://team-polive.firebaseio.com/')
  ref.remove()
}

// clear the firebase, so that the simulation always starts from no one
clear()

// run each second
setInterval(simulate, 2000)
<commit_msg>pseudo code for worker<commit_after>var _ = require('lodash')
var Firebase = require('firebase');
var names = ['Ahmed', 'Russell', 'Sigrunn', 'Juan', 'Tom', 'Jack', 'Somebody']

// simualate a random person entering, staying for a duration, and leaving
function simulate(){
  // generate a random person with a random name,
  // random location, and random duration
  var name = names[Math.random()%names.length]
  var vote = Math.random()%2
  var person = {
    name: name,
    vote: vote
  }
  // simulate this person entering
  enter(person)

  // simulate this person leaving after 'duration' seconds
  setTimeout(function(){
    leave(person)
  }, duration * 1000)

}

function enter(person){
  console.log('enter', person)
  // TODO: put this person in the Firebase
  // var ref = new Firebase('your-firebase-url')
  // ...
  var ref = new Firebase('https://team-polive.firebaseio.com/')
  ref.child(person.name).set({
    name: person.name,
  });
  ref.child(person.name.politics.vote).set({
    vote: person.vote,
  });
  var newTaskRef = ref.push()
}

function clear(){
  // TODO: remove all people from the Firebase
  // var ref = new Firebase('your-firebase-url')
  // ...
  // TODO: remove this person from the Firebase
  // var ref = new Firebase('your-firebase-url')
  var ref = new Firebase('https://team-polive.firebaseio.com/')
  ref.remove()
}

// clear the firebase, so that the simulation always starts from no one
clear()

// run each second
setInterval(simulate, 2000)
<|endoftext|>


<commit_before>var net = require('net');
var tls = require('tls');
var url = require('url');
var path = require('path');
var EventEmitter = require('events').EventEmitter;
var connect = require('../proxy/connect');
var util = require('../../util');
var rules = require('../rules');
var parseReq = require('../proxy/parse-req');
var cert = require('./cert');
var DEFAULT_CERT_CTX = cert.DEFAULT_CERT_CTX;
var DEFAULT_CERT = cert.getDefault();
var LOCALHOST = '127.0.0.1';

function getFullUrl(socket, wss) {
	return (wss ? 'wss:' : 'ws:') + '//' + socket.headers.host + socket.url;
}

function getRequestEmitter(socket, wss) {
	var reqEmitter = new EventEmitter();
	reqEmitter.url = getFullUrl(socket, wss);
	reqEmitter.ip = socket.remoteAddress;
	reqEmitter.method = socket.method;
	reqEmitter.headers = socket.headers;
	reqEmitter.httpVersion = socket.httpVersion;
	return reqEmitter;
}

function handleWebsocket(socket, wss, callback, app) {
	var config = app.config;
	var reqEmitter = app.reqEmitter;
	var headers = socket.headers;
	var host = headers.host;
	var fullUrl = socket.fullUrl = getFullUrl(socket, wss);
	var options = url.parse(fullUrl);
	var _rules = reqEmitter.rules = rules.resolveRules(fullUrl);
	var proxyUrl = util.rule.getMatcher(_rules.proxy);
	var reqSocket, options, matchedUrl;
	var now = Date.now();

	function execCallback(err, socket) {
		err && util.emitError(reqEmitter, err);
		callback(err, socket);
	}

	if (proxyUrl) {
		proxyUrl = (wss ? 'wss:' : 'ws:') + util.removeProtocol(proxyUrl);
		rules.resolveHost(proxyUrl, function(err, ip) {
			if (err || !ip) {
				return execCallback(err || new Error('lookup DNS faild: ' + proxyUrl));
			}

			options = url.parse(proxyUrl);
			socket.options = {
					host: ip,
					port: options.port,
					isWs: !wss
			};
			reqEmitter.host = ip;
			reqEmitter.dnsTime = Date.now() - now;
			reqEmitter.emit('send');
			connect(socket, function(err, proxySocket) {
				if (err) {
					return execCallback(err);
				}

				if (wss) {
					reqSocket = tls.connect({
				        rejectUnauthorized: false,
				        socket: proxySocket
				    }, pipeData);
				} else {
					reqSocket = proxySocket;
					pipeData();
				}

			});

		}, true);
		return;
	}

	matchedUrl = util.rule.getUrl(_rules.rule);
	if (/^wss?:\/\//.test(matchedUrl)) {
		reqEmitter.realUrl = fullUrl = matchedUrl;
	} else {
		matchedUrl = null;
	}

	options = url.parse(fullUrl);

	rules.resolveHost(fullUrl, function(err, ip) {
		if (err || !ip) {
			return execCallback(err || new Error('lookup DNS faild: ' + fullUrl));
		}
		reqEmitter.host = ip;
		reqEmitter.dnsTime = Date.now() - now;
		reqEmitter.emit('send');

		var isWss = options.protocol == 'wss';
		reqSocket = (isWss ? tls : net).connect({
			rejectUnauthorized: false,
			host: ip,
			port: options.port || (isWss ? 443 : 80)
		}, pipeData);

	}, true);

	function pipeData() {
		var headers = socket.headers;
		var origin;
		if (matchedUrl) {
			headers.host = options.host;
			origin = headers.origin;
			headers.origin = (options.protocol == 'wss' ? 'https://' : 'http://') + options.host;
		}

		reqSocket.write(socket.getBuffer(matchedUrl ? headers : null));
		socket.pipe(reqSocket);
		parseReq(reqSocket, function(err, res) {
			if (err) {
				return execCallback(err);
			}

			headers = res.headers;
			if (matchedUrl) {
				headers['access-control-allow-origin'] = origin;
			}
			socket.write(res.getBuffer(matchedUrl ? headers : null));
			res.pipe(socket);
			reqEmitter.emit('response', {
				headers: headers,
				realUrl: matchedUrl,
				statusCode: res.statusCode
			});
			execCallback(null, reqSocket);
		}, true);
	}
}

exports.dispatch = function dispatch(socket, app) {
	var config = app.config;
	var reqSocket, buffer;
	var content = '';

	function destroy() {
		socket.destroy();
		reqSocket && reqSocket.destroy();
	}

	function abortIfUnavailable(socket) {
		return socket.on('error', destroy)
			.on('close', destroy);
	}

	abortIfUnavailable(socket);
	socket.on('data', request);
	socket.on('end', request);

	function request(chunk) {
		socket.removeListener('data', request);
		socket.removeListener('end', request);
		if (!chunk) {//没有数据
			return destroy();
		}

		buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
		if (/upgrade\s*:\s*websocket/i.test(buffer && buffer.toString())) { //ws
			parseReq(socket, function(err, socket) {
				if (err) {
					return destroy();
				}
				var reqEmitter = getRequestEmitter(socket);
				app.emit('websocket', reqEmitter);
				app.reqEmitter = reqEmitter;
				handleWebsocket(socket, false, function(err, req) {
					if (err) {
						return destroy();
					}
					reqSocket = req;
					abortIfUnavailable(reqSocket);
				}, app);
			}, buffer, true);
			buffer = null;
		} else {
			reqSocket = net.connect(config.tlsport, LOCALHOST, function() {
				reqSocket.write(buffer);
	            reqSocket.pipe(socket).pipe(reqSocket);
	            buffer = null;
	        });
			abortIfUnavailable(reqSocket);
		}
	}

};

exports.createServer = function createServer(app) {
	var config = app.config;
	var server = tls.createServer({
		SNICallback: function(hostname, callback) {
			if (typeof callback != 'function') {
				return cert.getCert();
			}

			callback(null, cert.getCert());
		},
		key: DEFAULT_CERT[0],
		cert: DEFAULT_CERT[1]
	},function(socket) {
		var reqSocket;
		function destroy() {
			socket.destroy();
			reqSocket && reqSocket.destroy();
		}

		function abortIfUnavailable(socket) {
			return socket.on('error', destroy)
				.on('close', destroy);
		}

		abortIfUnavailable(socket);
		parseReq(socket, function(err, socket) {
			if (err) {
				return destroy();
			}
			//wss

			var headers = socket.headers;
			if (headers.upgrade && headers.upgrade.toLowerCase() == 'websocket') {
				var reqEmitter = getRequestEmitter(socket, true);
				app.emit('websocket', reqEmitter);
				app.reqEmitter = reqEmitter;
				handleWebsocket(socket, true, function(err, req) {
					if (err) {
						return destroy();
					}
					reqSocket = req;
					abortIfUnavailable(reqSocket);
				}, app);
			} else {
				//https
				reqSocket = net.connect(config.port, LOCALHOST, function() {
					headers[util.HTTPS_FIELD] = 1;
					reqSocket.write(socket.getBuffer(headers));
					socket.pipe(reqSocket).pipe(socket);
		        });
				abortIfUnavailable(reqSocket);
			}
		}, true);

	}).listen(config.tlsport, 'localhost')
		.on('error', util.noop);
};
<commit_msg>remove ctx<commit_after>var net = require('net');
var tls = require('tls');
var url = require('url');
var path = require('path');
var EventEmitter = require('events').EventEmitter;
var connect = require('../proxy/connect');
var util = require('../../util');
var rules = require('../rules');
var parseReq = require('../proxy/parse-req');
var cert = require('./cert');
var DEFAULT_CERT = cert.getDefault();
var LOCALHOST = '127.0.0.1';

function getFullUrl(socket, wss) {
	return (wss ? 'wss:' : 'ws:') + '//' + socket.headers.host + socket.url;
}

function getRequestEmitter(socket, wss) {
	var reqEmitter = new EventEmitter();
	reqEmitter.url = getFullUrl(socket, wss);
	reqEmitter.ip = socket.remoteAddress;
	reqEmitter.method = socket.method;
	reqEmitter.headers = socket.headers;
	reqEmitter.httpVersion = socket.httpVersion;
	return reqEmitter;
}

function handleWebsocket(socket, wss, callback, app) {
	var config = app.config;
	var reqEmitter = app.reqEmitter;
	var headers = socket.headers;
	var host = headers.host;
	var fullUrl = socket.fullUrl = getFullUrl(socket, wss);
	var options = url.parse(fullUrl);
	var _rules = reqEmitter.rules = rules.resolveRules(fullUrl);
	var proxyUrl = util.rule.getMatcher(_rules.proxy);
	var reqSocket, options, matchedUrl;
	var now = Date.now();

	function execCallback(err, socket) {
		err && util.emitError(reqEmitter, err);
		callback(err, socket);
	}

	if (proxyUrl) {
		proxyUrl = (wss ? 'wss:' : 'ws:') + util.removeProtocol(proxyUrl);
		rules.resolveHost(proxyUrl, function(err, ip) {
			if (err || !ip) {
				return execCallback(err || new Error('lookup DNS faild: ' + proxyUrl));
			}

			options = url.parse(proxyUrl);
			socket.options = {
					host: ip,
					port: options.port,
					isWs: !wss
			};
			reqEmitter.host = ip;
			reqEmitter.dnsTime = Date.now() - now;
			reqEmitter.emit('send');
			connect(socket, function(err, proxySocket) {
				if (err) {
					return execCallback(err);
				}

				if (wss) {
					reqSocket = tls.connect({
				        rejectUnauthorized: false,
				        socket: proxySocket
				    }, pipeData);
				} else {
					reqSocket = proxySocket;
					pipeData();
				}

			});

		}, true);
		return;
	}

	matchedUrl = util.rule.getUrl(_rules.rule);
	if (/^wss?:\/\//.test(matchedUrl)) {
		reqEmitter.realUrl = fullUrl = matchedUrl;
	} else {
		matchedUrl = null;
	}

	options = url.parse(fullUrl);

	rules.resolveHost(fullUrl, function(err, ip) {
		if (err || !ip) {
			return execCallback(err || new Error('lookup DNS faild: ' + fullUrl));
		}
		reqEmitter.host = ip;
		reqEmitter.dnsTime = Date.now() - now;
		reqEmitter.emit('send');

		var isWss = options.protocol == 'wss';
		reqSocket = (isWss ? tls : net).connect({
			rejectUnauthorized: false,
			host: ip,
			port: options.port || (isWss ? 443 : 80)
		}, pipeData);

	}, true);

	function pipeData() {
		var headers = socket.headers;
		var origin;
		if (matchedUrl) {
			headers.host = options.host;
			origin = headers.origin;
			headers.origin = (options.protocol == 'wss' ? 'https://' : 'http://') + options.host;
		}

		reqSocket.write(socket.getBuffer(matchedUrl ? headers : null));
		socket.pipe(reqSocket);
		parseReq(reqSocket, function(err, res) {
			if (err) {
				return execCallback(err);
			}

			headers = res.headers;
			if (matchedUrl) {
				headers['access-control-allow-origin'] = origin;
			}
			socket.write(res.getBuffer(matchedUrl ? headers : null));
			res.pipe(socket);
			reqEmitter.emit('response', {
				headers: headers,
				realUrl: matchedUrl,
				statusCode: res.statusCode
			});
			execCallback(null, reqSocket);
		}, true);
	}
}

exports.dispatch = function dispatch(socket, app) {
	var config = app.config;
	var reqSocket, buffer;
	var content = '';

	function destroy() {
		socket.destroy();
		reqSocket && reqSocket.destroy();
	}

	function abortIfUnavailable(socket) {
		return socket.on('error', destroy)
			.on('close', destroy);
	}

	abortIfUnavailable(socket);
	socket.on('data', request);
	socket.on('end', request);

	function request(chunk) {
		socket.removeListener('data', request);
		socket.removeListener('end', request);
		if (!chunk) {//没有数据
			return destroy();
		}

		buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
		if (/upgrade\s*:\s*websocket/i.test(buffer && buffer.toString())) { //ws
			parseReq(socket, function(err, socket) {
				if (err) {
					return destroy();
				}
				var reqEmitter = getRequestEmitter(socket);
				app.emit('websocket', reqEmitter);
				app.reqEmitter = reqEmitter;
				handleWebsocket(socket, false, function(err, req) {
					if (err) {
						return destroy();
					}
					reqSocket = req;
					abortIfUnavailable(reqSocket);
				}, app);
			}, buffer, true);
			buffer = null;
		} else {
			reqSocket = net.connect(config.tlsport, LOCALHOST, function() {
				reqSocket.write(buffer);
	            reqSocket.pipe(socket).pipe(reqSocket);
	            buffer = null;
	        });
			abortIfUnavailable(reqSocket);
		}
	}

};

exports.createServer = function createServer(app) {
	var config = app.config;
	var server = tls.createServer({
		SNICallback: function(hostname, callback) {
			if (typeof callback != 'function') {
				return cert.getCert();
			}

			callback(null, cert.getCert());
		},
		key: DEFAULT_CERT[0],
		cert: DEFAULT_CERT[1]
	},function(socket) {
		var reqSocket;
		function destroy() {
			socket.destroy();
			reqSocket && reqSocket.destroy();
		}

		function abortIfUnavailable(socket) {
			return socket.on('error', destroy)
				.on('close', destroy);
		}

		abortIfUnavailable(socket);
		parseReq(socket, function(err, socket) {
			if (err) {
				return destroy();
			}
			//wss

			var headers = socket.headers;
			if (headers.upgrade && headers.upgrade.toLowerCase() == 'websocket') {
				var reqEmitter = getRequestEmitter(socket, true);
				app.emit('websocket', reqEmitter);
				app.reqEmitter = reqEmitter;
				handleWebsocket(socket, true, function(err, req) {
					if (err) {
						return destroy();
					}
					reqSocket = req;
					abortIfUnavailable(reqSocket);
				}, app);
			} else {
				//https
				reqSocket = net.connect(config.port, LOCALHOST, function() {
					headers[util.HTTPS_FIELD] = 1;
					reqSocket.write(socket.getBuffer(headers));
					socket.pipe(reqSocket).pipe(socket);
		        });
				abortIfUnavailable(reqSocket);
			}
		}, true);

	}).listen(config.tlsport, 'localhost')
		.on('error', util.noop);
};
<|endoftext|>


<commit_before>"use strict";

var _ = require('lodash');
var i8n = require('inflection');
var path = require('path');
var resolve =require('resolve');
var Schema = require('./schema');

module.exports = Registry;

function Registry(context) {
    if (!this instanceof Registry) return new Registry(context);

    this.context = context || this;
    this.schemas = [];
    this.definitions = {};
    this.models = {};
    this.plugins = [];
}

Registry.prototype.use = function (plugin, opts) {
    if (typeof plugin === "string") {
        try {
            plugin = require(resolve.sync(plugin, { basedir: path.resolve('./') }));
        } catch (e) {
            throw e;
        }
    }

    plugin = new plugin(this, opts || {});

    if (typeof plugin.define === "function") {
        for (var k in this.models) {
            plugin.define(this.models[k]);
        }
    }

    this.plugins.push(plugin);

    return this;
};

Registry.prototype.define = function (/*name, desc, setup*/) {
    var i, arg, type, name, desc, setup;
    for (i = 0; i < arguments.length; i++) {
        arg = arguments[i];
        type = typeof arg;
        if (!name && type === 'string') {
            name = arg;
        } else if (!desc && (type === 'object' || type === 'function')) {
            desc = arg;
        } else if (!setup && type === 'function') {
            setup = arg;
        } else {
            throw new Error('Unsupported argument ' + arg);
        }
    }

    desc = desc || {};
    if (typeof desc === 'function') {
        desc = desc(Schema);
    }

    if (desc.name) {
        name = desc.name;
    }

    var def;

    desc = standartize(desc);
    def = findDefinition(this.definitions, name);
    if (def) {
        name = def.name;
        desc = _.merge(_.cloneDeep(def), desc);
    } else {
        desc.name = name;
    }
    this.definitions[name] = desc;
    this.setup(desc, setup);
    return desc;
};

var DEF_KEYS = ['name', 'properties', 'fields', 'relations', 'settings'];
function standartize(desc) {
    var result = _.pick(desc, DEF_KEYS);
    result.settings = _.assign({}, result.settings, _.omit(desc, DEF_KEYS));
    return result;
}

Registry.prototype.setup = function (name, setup) {
    if (typeof setup !== 'function') return;
    var def = findDefinition(this.definitions, name);

    if (!def) throw new Error('Unknown model ' + name + ' for setup');

    def.setups = def.setups || [];
    if (Array.isArray(setup)) {
        def.setups = def.setups.concat(setup);
    } else {
        def.setups.push(setup);
    }
};

Registry.prototype.build = function (options) {
    if (!options) {
        options = {db: {driver: 'memory'}};
    } else if (typeof options === 'string') {
        options = {db: {driver: options}};
    } else if (options.driver) {
        options = {db: options};
    }

    if (!_.isEmpty(options)) throw new Error('No database config provided');

    var self = this;
    var schemas = this.schemas = [];
    this.models = {};

    _.forEach(options, function (conf, name) {
        if (!conf || !conf.driver) {
            console.log('No config found for ' + name + ' schema, using in-memory schema');
            conf = {driver: 'memory'};
        }
        var schema = createSchema(conf.driver, conf);
        self._build(schema, conf.models);
        schema.name = name;
        schemas[name] = schema;
        schemas.push(schema);
    });

    return schemas;
};

Registry.prototype._build = function (schema, names) {
    if (names == '*') names = null;
    if (typeof names === 'string') names = [names];
    if (names && !Array.isArray(names)) {
        throw new Error('`names` must be a string or string array');
    }

    var context = this.context;
    var plugins = this.plugins;
    var models = this.models;
    var defs = names ? _.pick(this.definitions, names) : this.definitions;

    schema.on('beforeDefine', function(name, properties, settings) {
        for (var i = 0; i < plugins.length; i++) {
            if (typeof plugins[i].beforeDefine === "function") {
                plugins[i].beforeDefine(name, properties, settings);
            }
        }
    });

    schema.on('define', function(model, name, properties, settings) {
        models[name] = model;
        if (schema.backyard) {
            schema.backyard.define(name, properties, settings);
        }
        for (var i = 0; i < plugins.length; i++) {
            if (typeof plugins[i].define === "function") {
                plugins[i].define(model, name, properties, settings);
            }
        }
    });

    _.forEach(defs, function (def, name) {
        schema.define(name, def.properties || def.fields, def.settings);
    });

    _.forEach(defs, function (def) {
        buildRelations(schema, def);
    });

    _.forEach(defs, function (def) {
        if (!def.setups) return;
        for(var i = 0; i < def.setups.length; i++) {
            def.setups[i](models[def.name], context);
        }
    });
};

function findDefinition(definitions, name) {
    if (typeof name === 'object') return name;
    var possibles = [name, name.toLowerCase(), name.toUpperCase(), i8n.underscore(name), i8n.camelize(name)],
        i;
    for (i = 0; i < possibles.length; i++) {
        if (definitions[possibles[i]]) return definitions[possibles[i]];
    }
}

function createSchema(name, settings) {
    var schema = new Schema(name, settings);
    if (settings && settings.backyard) {
        schema.backyard = new Schema(settings.backyard.driver, settings.backyard);
    }
    return schema;
}

function buildRelations(schema, def) {
    if (!def.relations) return;
    var model = schema.models[def.name];
    var relations = def.relations;
    for (var name in relations) {
        var rel = relations[name];
        var target = rel.model,
            targetName,
            targetModel;

        if (!target) {
            target = i8n.singularize(name);
        }
        targetName = findKey(schema.models, target);
        if (targetName) {
            targetModel = schema.models[targetName];
        }

        if (!targetModel) {
            throw new Error(util.format('Unknown target model `%s` to build relationship %s.%s -> %s -> %s',
                target, model.modelName, name, rel.type, target));
        }

        var options = _.omit(rel, ['type', 'target', 'model']);
        options.as = options.as || name;
        model[rel.type](targetModel, options);
    }
}

function findKey(object, target) {
    var targetName = target.toLowerCase();
    return _.findKey(object, function (value, name) {
        return name.toLowerCase() === targetName;
    });
}
<commit_msg>check options empty for registry.build used in database boot phase<commit_after>"use strict";

var _ = require('lodash');
var i8n = require('inflection');
var path = require('path');
var resolve =require('resolve');
var Schema = require('./schema');

module.exports = Registry;

function Registry(context) {
    if (!this instanceof Registry) return new Registry(context);

    this.context = context || this;
    this.schemas = [];
    this.definitions = {};
    this.models = {};
    this.plugins = [];
}

Registry.prototype.use = function (plugin, opts) {
    if (typeof plugin === "string") {
        try {
            plugin = require(resolve.sync(plugin, { basedir: path.resolve('./') }));
        } catch (e) {
            throw e;
        }
    }

    plugin = new plugin(this, opts || {});

    if (typeof plugin.define === "function") {
        for (var k in this.models) {
            plugin.define(this.models[k]);
        }
    }

    this.plugins.push(plugin);

    return this;
};

Registry.prototype.define = function (/*name, desc, setup*/) {
    var i, arg, type, name, desc, setup;
    for (i = 0; i < arguments.length; i++) {
        arg = arguments[i];
        type = typeof arg;
        if (!name && type === 'string') {
            name = arg;
        } else if (!desc && (type === 'object' || type === 'function')) {
            desc = arg;
        } else if (!setup && type === 'function') {
            setup = arg;
        } else {
            throw new Error('Unsupported argument ' + arg);
        }
    }

    desc = desc || {};
    if (typeof desc === 'function') {
        desc = desc(Schema);
    }

    if (desc.name) {
        name = desc.name;
    }

    var def;

    desc = standartize(desc);
    def = findDefinition(this.definitions, name);
    if (def) {
        name = def.name;
        desc = _.merge(_.cloneDeep(def), desc);
    } else {
        desc.name = name;
    }
    this.definitions[name] = desc;
    this.setup(desc, setup);
    return desc;
};

var DEF_KEYS = ['name', 'properties', 'fields', 'relations', 'settings'];
function standartize(desc) {
    var result = _.pick(desc, DEF_KEYS);
    result.settings = _.assign({}, result.settings, _.omit(desc, DEF_KEYS));
    return result;
}

Registry.prototype.setup = function (name, setup) {
    if (typeof setup !== 'function') return;
    var def = findDefinition(this.definitions, name);

    if (!def) throw new Error('Unknown model ' + name + ' for setup');

    def.setups = def.setups || [];
    if (Array.isArray(setup)) {
        def.setups = def.setups.concat(setup);
    } else {
        def.setups.push(setup);
    }
};

Registry.prototype.build = function (options) {
    if (!options) {
        options = {db: {driver: 'memory'}};
    } else if (typeof options === 'string') {
        options = {db: {driver: options}};
    } else if (options.driver) {
        options = {db: options};
    }

    if (_.isEmpty(options)) throw new Error('No database config provided');

    var self = this;
    var schemas = this.schemas = [];
    this.models = {};

    _.forEach(options, function (conf, name) {
        if (!conf || !conf.driver) {
            console.log('No config found for ' + name + ' schema, using in-memory schema');
            conf = {driver: 'memory'};
        }
        var schema = createSchema(conf.driver, conf);
        self._build(schema, conf.models);
        schema.name = name;
        schemas[name] = schema;
        schemas.push(schema);
    });

    return schemas;
};

Registry.prototype._build = function (schema, names) {
    if (names == '*') names = null;
    if (typeof names === 'string') names = [names];
    if (names && !Array.isArray(names)) {
        throw new Error('`names` must be a string or string array');
    }

    var context = this.context;
    var plugins = this.plugins;
    var models = this.models;
    var defs = names ? _.pick(this.definitions, names) : this.definitions;

    schema.on('beforeDefine', function(name, properties, settings) {
        for (var i = 0; i < plugins.length; i++) {
            if (typeof plugins[i].beforeDefine === "function") {
                plugins[i].beforeDefine(name, properties, settings);
            }
        }
    });

    schema.on('define', function(model, name, properties, settings) {
        models[name] = model;
        if (schema.backyard) {
            schema.backyard.define(name, properties, settings);
        }
        for (var i = 0; i < plugins.length; i++) {
            if (typeof plugins[i].define === "function") {
                plugins[i].define(model, name, properties, settings);
            }
        }
    });

    _.forEach(defs, function (def, name) {
        schema.define(name, def.properties || def.fields, def.settings);
    });

    _.forEach(defs, function (def) {
        buildRelations(schema, def);
    });

    _.forEach(defs, function (def) {
        if (!def.setups) return;
        for(var i = 0; i < def.setups.length; i++) {
            def.setups[i](models[def.name], context);
        }
    });
};

function findDefinition(definitions, name) {
    if (typeof name === 'object') return name;
    var possibles = [name, name.toLowerCase(), name.toUpperCase(), i8n.underscore(name), i8n.camelize(name)],
        i;
    for (i = 0; i < possibles.length; i++) {
        if (definitions[possibles[i]]) return definitions[possibles[i]];
    }
}

function createSchema(name, settings) {
    var schema = new Schema(name, settings);
    if (settings && settings.backyard) {
        schema.backyard = new Schema(settings.backyard.driver, settings.backyard);
    }
    return schema;
}

function buildRelations(schema, def) {
    if (!def.relations) return;
    var model = schema.models[def.name];
    var relations = def.relations;
    for (var name in relations) {
        var rel = relations[name];
        var target = rel.model,
            targetName,
            targetModel;

        if (!target) {
            target = i8n.singularize(name);
        }
        targetName = findKey(schema.models, target);
        if (targetName) {
            targetModel = schema.models[targetName];
        }

        if (!targetModel) {
            throw new Error(util.format('Unknown target model `%s` to build relationship %s.%s -> %s -> %s',
                target, model.modelName, name, rel.type, target));
        }

        var options = _.omit(rel, ['type', 'target', 'model']);
        options.as = options.as || name;
        model[rel.type](targetModel, options);
    }
}

function findKey(object, target) {
    var targetName = target.toLowerCase();
    return _.findKey(object, function (value, name) {
        return name.toLowerCase() === targetName;
    });
}
<|endoftext|>


<commit_before>const fs = require("fs");
const { join } = require("path");
const { DateTime } = require("luxon");
const { oauth2, sa } = require("../settings.js");
const log = require("debug")("niles:guilds");
const util = require("util");

const emptyCal = {
  "day0": [],
  "day1": [],
  "day2": [],
  "day3": [],
  "day4": [],
  "day5": [],
  "day6": [],
  "lastUpdate": "",
  "calendarMessageId": ""
};

// default guild settings
const defaultSettings = {
  "prefix": "!",
  "calendarID": "",
  "calendarChannel": "",
  "calendarName": "CALENDAR",
  "timezone": "",
  "helpmenu": "1",
  "format": 12,
  "tzDisplay": "0",
  "allowedRoles": [],
  "emptydays": "1",
  "showpast": "0",
  "trim": 0,
  "days": 7,
  "style": "code",
  "inline": "0",
  "description": "0",
  "url": "0",
  "auth": "sa",
  "channelid": "",
  "descLength": 0,
  "startonly": "0",
  "eventtime": "1",
  "lng": "en"
};

/**
 * Delete folder recursively
 * @param {String} path
 */
function deleteFolderRecursive(path) {
  if (fs.existsSync(path)) {
    fs.rmdirSync(path, {recursive: true});
  }
}

/**
 * Writes guild-specific setting
 * @param {String} guildID - ID of guild to write setting to
 * @param {Object} json - json array of values to write
 * @param {String} file - file name to write to - calendar/settings
 */
function writeGuildSpecific(guildID, json, file) {
  log(`writeGuildSpecific | ${guildID} | file: ${file}`);
  let fullPath = join(__dirname, "..", "stores", guildID, file + ".json");
  fs.writeFile(fullPath, JSON.stringify(json, "", "\t"), (err) => {
    if (err) return log("error writing guild specific database: " + err);
  });
}

/**
 * Create new guild files
 * @param {String} guildID - Guild to create files for
 */
function createGuild(guildID) {
  const guildPath = join(__dirname, "..", "stores", guildID);
  if (!fs.existsSync(guildPath)) { // create directory and new files
    fs.mkdirSync(guildPath);
    writeGuildSpecific(guildID, emptyCal, "calendar");
    writeGuildSpecific(guildID, defaultSettings, "settings");
    log(`Guild ${guildID} has been created`);
  }
}

/**
 * Delete guild settings
 * @param {String} guildID - guild to delete configuration for
 */
function deleteGuild(guildID) {
  const guildPath = join(__dirname, "..", "stores", guildID);
  deleteFolderRecursive(guildPath);
  log(`Guild ${guildID} has been deleted`);
}

/**
 * Delete and recreate guild settings
 * @param {StringDecoder} guildID
 */
function recreateGuild(guildID) {
  deleteGuild(guildID);
  createGuild(guildID);
}

/**
 * Try and read file
 * @param {String} path - path of file to read
 */
function readFile(path) {
  try { return JSON.parse(fs.readFileSync(path, "utf8"));
  } catch (err) {
    log("error reading file " + err);
    return {}; // return valid JSON to trigger update
  }
}

/**
 * Get guild-specific file
 * @param {String} guildID
 * @param {String} file
 */
function getGuildSpecific(guildID, file) {
  log(`getGuildSpecific | ${guildID} | file: ${file}`);
  let filePath = join(__dirname, "..", "stores", guildID, file);
  let storedData = readFile(filePath);
  // merge defaults and stored settings to guarantee valid data - only for settings
  // if settings - merge defaults and stored
  // if calendar - send default if empty
  // otherwise return normal
  if (file === "settings.json") return {...defaultSettings, ...storedData};
  if (!Object.entries(storedData).length) {
    log(`empty file for guild ${guildID} - check logs`);
    console.error(`empty file for ${guildID} - ${util.inspect(storedData)}`);
  }
  return storedData;
}

/**
 * Generate daymap
 * @param {Object} settings - guildsettings to build around
 * @returns {dayMap} - dayMap for guild
 */
function generateDayMap(settings) {
  let dayMap = [];
  // allowing all days to be correctly TZ adjusted
  let d = DateTime.fromJSDate(new Date()).setZone(settings.timezone);
  // if Option to show past events is set, start at startOf Day instead of NOW()
  if (settings.showpast === "1") d = d.startOf("day");
  dayMap[0] =  d;
  for (let i = 1; i < settings.days; i++) {
    dayMap[i] = d.plus({ days: i }); //DateTime is immutable, this creates new objects!
  }
  return dayMap;
}

/**
 * @class
 */
function Guild(guildID) {
  // Load Settings
  this.settings = getGuildSpecific(guildID, "settings.json");
  /**
   * Get specific or all settings
   * @param {String} [key] - Optional key to fetch
   */
  this.getSetting = (key) => (key ? this.settings[key] : this.settings);
  /**
   * Sets specific setting to value
   * @param {String} key - key of setting to change
   * @param {String} value - value to set key to
   */
  this.setSetting = (key, value) => {
    this.settings[key] = value;
    writeGuildSpecific(guildID, this.settings, "settings");
  };
  // common properties
  this.prefix = this.settings.prefix;
  this.id = guildID;
  this.tz = this.settings.timezone;
  this.lng = this.settings.lng;
  // calendar
  this.calendar = getGuildSpecific(guildID, "calendar.json");
  /**
   * Get calendar file
   * @param {String} [key] - Optionally get specific key
   */
  this.getCalendar = (key) => (key ? this.calendar[key] : this.calendar);
  // Write current calendar file
  this.writeCalendar = () => writeGuildSpecific(guildID, this.calendar, "calendar");
  /**
   * Set calendar last update
   * @param {String} date
   */
  this.setCalendarLastUpdate = (date) => {
    this.calendar.lastUpdate = date;
    this.writeCalendar();
  };
  /**
   * Set Calendar day to value
   * @param {Integer} day - Key to change
   * @param {[Object]} events - events to set
   */
  this.setCalendarDay = (day, events) => {
    let key = "day" + String(day);
    this.calendar[key] = events;
    this.writeCalendar();
  };
  // calendarID
  /**
   * Set Calendar Message ID
   * @param {String} calendarID - ID of calendar message
   */
  this.setCalendarID = (calendarID) => {
    this.calendar.calendarMessageId = calendarID;
    this.writeCalendar();
  };
  // generate daymap
  this.getDayMap = () => generateDayMap(this.settings);
  // get OAuth2 Token
  this.getToken = () => getGuildSpecific(guildID, "token.json");
  /**
   * Set OAuth2 token
   * @param {Object} token - token object to write
   */
  this.setToken = (token) => writeGuildSpecific(guildID, token, "token");
  /**
   * Gets guild authentication
   * @returns return googleAuth object
   */
  this.getAuth = () => {
    if (this.settings.auth === "oauth") {
      oauth2.setCredentials(this.getToken());
      return oauth2;
    // default to SA if oauth2 failed too
    } else { return sa;
    }
  };
  /**
   * Update settings and calendar
   */
  this.update = () => {
    log(`Guild.update | ${guildID}`);
    this.settings = getGuildSpecific(guildID, "settings.json");
    this.calendar = getGuildSpecific(guildID, "calendar.json");
  };
}

module.exports = {
  Guild,
  createGuild,
  deleteGuild,
  recreateGuild
};<commit_msg>revert verbose logging<commit_after>const fs = require("fs");
const { join } = require("path");
const { DateTime } = require("luxon");
const { oauth2, sa } = require("../settings.js");
const log = require("debug")("niles:guilds");

const emptyCal = {
  "day0": [],
  "day1": [],
  "day2": [],
  "day3": [],
  "day4": [],
  "day5": [],
  "day6": [],
  "lastUpdate": "",
  "calendarMessageId": ""
};

// default guild settings
const defaultSettings = {
  "prefix": "!",
  "calendarID": "",
  "calendarChannel": "",
  "calendarName": "CALENDAR",
  "timezone": "",
  "helpmenu": "1",
  "format": 12,
  "tzDisplay": "0",
  "allowedRoles": [],
  "emptydays": "1",
  "showpast": "0",
  "trim": 0,
  "days": 7,
  "style": "code",
  "inline": "0",
  "description": "0",
  "url": "0",
  "auth": "sa",
  "channelid": "",
  "descLength": 0,
  "startonly": "0",
  "eventtime": "1",
  "lng": "en"
};

/**
 * Delete folder recursively
 * @param {String} path
 */
function deleteFolderRecursive(path) {
  if (fs.existsSync(path)) {
    fs.rmdirSync(path, {recursive: true});
  }
}

/**
 * Writes guild-specific setting
 * @param {String} guildID - ID of guild to write setting to
 * @param {Object} json - json array of values to write
 * @param {String} file - file name to write to - calendar/settings
 */
function writeGuildSpecific(guildID, json, file) {
  log(`writeGuildSpecific | ${guildID} | file: ${file}`);
  let fullPath = join(__dirname, "..", "stores", guildID, file + ".json");
  fs.writeFile(fullPath, JSON.stringify(json, "", "\t"), (err) => {
    if (err) return log("error writing guild specific database: " + err);
  });
}

/**
 * Create new guild files
 * @param {String} guildID - Guild to create files for
 */
function createGuild(guildID) {
  const guildPath = join(__dirname, "..", "stores", guildID);
  if (!fs.existsSync(guildPath)) { // create directory and new files
    fs.mkdirSync(guildPath);
    writeGuildSpecific(guildID, emptyCal, "calendar");
    writeGuildSpecific(guildID, defaultSettings, "settings");
    log(`Guild ${guildID} has been created`);
  }
}

/**
 * Delete guild settings
 * @param {String} guildID - guild to delete configuration for
 */
function deleteGuild(guildID) {
  const guildPath = join(__dirname, "..", "stores", guildID);
  deleteFolderRecursive(guildPath);
  log(`Guild ${guildID} has been deleted`);
}

/**
 * Delete and recreate guild settings
 * @param {StringDecoder} guildID
 */
function recreateGuild(guildID) {
  deleteGuild(guildID);
  createGuild(guildID);
}

/**
 * Try and read file
 * @param {String} path - path of file to read
 */
function readFile(path) {
  try { return JSON.parse(fs.readFileSync(path, "utf8"));
  } catch (err) {
    log("error reading file " + err);
    return {}; // return valid JSON to trigger update
  }
}

/**
 * Get guild-specific file
 * @param {String} guildID
 * @param {String} file
 */
function getGuildSpecific(guildID, file) {
  log(`getGuildSpecific | ${guildID} | file: ${file}`);
  let filePath = join(__dirname, "..", "stores", guildID, file);
  let storedData = readFile(filePath);
  // merge defaults and stored settings to guarantee valid data - only for settings
  // if settings - merge defaults and stored
  // if calendar - send default if empty
  // otherwise return normal
  return (file === "settings.json") ? {...defaultSettings, ...storedData}
    : (file === "calendar.json" && !Object.entries(storedData).length) ? emptyCal
      : storedData;
}

/**
 * Generate daymap
 * @param {Object} settings - guildsettings to build around
 * @returns {dayMap} - dayMap for guild
 */
function generateDayMap(settings) {
  let dayMap = [];
  // allowing all days to be correctly TZ adjusted
  let d = DateTime.fromJSDate(new Date()).setZone(settings.timezone);
  // if Option to show past events is set, start at startOf Day instead of NOW()
  if (settings.showpast === "1") d = d.startOf("day");
  dayMap[0] =  d;
  for (let i = 1; i < settings.days; i++) {
    dayMap[i] = d.plus({ days: i }); //DateTime is immutable, this creates new objects!
  }
  return dayMap;
}

/**
 * @class
 */
function Guild(guildID) {
  // Load Settings
  this.settings = getGuildSpecific(guildID, "settings.json");
  /**
   * Get specific or all settings
   * @param {String} [key] - Optional key to fetch
   */
  this.getSetting = (key) => (key ? this.settings[key] : this.settings);
  /**
   * Sets specific setting to value
   * @param {String} key - key of setting to change
   * @param {String} value - value to set key to
   */
  this.setSetting = (key, value) => {
    this.settings[key] = value;
    writeGuildSpecific(guildID, this.settings, "settings");
  };
  // common properties
  this.prefix = this.settings.prefix;
  this.id = guildID;
  this.tz = this.settings.timezone;
  this.lng = this.settings.lng;
  // calendar
  this.calendar = getGuildSpecific(guildID, "calendar.json");
  /**
   * Get calendar file
   * @param {String} [key] - Optionally get specific key
   */
  this.getCalendar = (key) => (key ? this.calendar[key] : this.calendar);
  // Write current calendar file
  this.writeCalendar = () => writeGuildSpecific(guildID, this.calendar, "calendar");
  /**
   * Set calendar last update
   * @param {String} date
   */
  this.setCalendarLastUpdate = (date) => {
    this.calendar.lastUpdate = date;
    this.writeCalendar();
  };
  /**
   * Set Calendar day to value
   * @param {Integer} day - Key to change
   * @param {[Object]} events - events to set
   */
  this.setCalendarDay = (day, events) => {
    let key = "day" + String(day);
    this.calendar[key] = events;
    this.writeCalendar();
  };
  // calendarID
  /**
   * Set Calendar Message ID
   * @param {String} calendarID - ID of calendar message
   */
  this.setCalendarID = (calendarID) => {
    this.calendar.calendarMessageId = calendarID;
    this.writeCalendar();
  };
  // generate daymap
  this.getDayMap = () => generateDayMap(this.settings);
  // get OAuth2 Token
  this.getToken = () => getGuildSpecific(guildID, "token.json");
  /**
   * Set OAuth2 token
   * @param {Object} token - token object to write
   */
  this.setToken = (token) => writeGuildSpecific(guildID, token, "token");
  /**
   * Gets guild authentication
   * @returns return googleAuth object
   */
  this.getAuth = () => {
    if (this.settings.auth === "oauth") {
      oauth2.setCredentials(this.getToken());
      return oauth2;
    // default to SA if oauth2 failed too
    } else { return sa;
    }
  };
  /**
   * Update settings and calendar
   */
  this.update = () => {
    log(`Guild.update | ${guildID}`);
    this.settings = getGuildSpecific(guildID, "settings.json");
    this.calendar = getGuildSpecific(guildID, "calendar.json");
  };
}

module.exports = {
  Guild,
  createGuild,
  deleteGuild,
  recreateGuild
};<|endoftext|>


<commit_before>/// <reference path="Animation.js"/>
/// CODE

//#region AnimationUsingKeyFrames
var AnimationUsingKeyFrames = Nullstone.Create("AnimationUsingKeyFrames", Animation);

AnimationUsingKeyFrames.Instance.Init = function () {
    this.Init$Animation();
};

//#region Properties

Nullstone.AbstractProperty(AnimationUsingKeyFrames, "KeyFrames");

//#endregion

AnimationUsingKeyFrames.Instance.Resolve = function (target, propd) {
    /// <param name="target" type="DependencyObject"></param>
    /// <param name="propd" type="DependencyProperty"></param>
    var keyFrames = this.KeyFrames;

    KeyFrameCollection.ResolveKeyFrames(this, keyFrames);

    var count = keyFrames._SortedList.length;
    for (var j = 0; j < count; j++) {
        if (!keyFrames._SortedList[j].KeyTime.IsValid)
            return false;
    }

    return true;
};
AnimationUsingKeyFrames.Instance.GetCurrentValue = function (defaultOriginValue, defaultDestinationValue, clockData) {
    var keyFrames = this.KeyFrames;

    var prevFrameRef = {};
    var currentKeyFrame = keyFrames.GetKeyFrameForTime(clockData.CurrentTime, prevFrameRef);
    var prevFrame = prevFrameRef.Value;
    if (currentKeyFrame == null)
        return null;

    var baseValue;
    var keyStartTime;
    var keyEndTime = currentKeyFrame._ResolvedKeyTime;
    if (prevFrame == null) {
        // the first keyframe, start at the animation's base value
        baseValue = defaultOriginValue;
        keyStartTime = new TimeSpan();
    } else {
        // start at the previous keyframe's target value
        baseValue = prevFrame.ConvertedValue;
        keyStartTime = prevFrame._ResolvedKeyTime;
    }

    var progress;
    if (clockData.CurrentTime.CompareTo(keyEndTime) >= 0) {
        progress = 1.0;
    } else {
        var keyDuration = keyEndTime._Ticks - keyStartTime._Ticks;
        if (keyDuration <= 0)
            progress = 1.0;
        else
            progress = (clockData.CurrentTime._Ticks - keyStartTime._Ticks) / keyDuration;
    }

    return currentKeyFrame.InterpolateValue(baseValue, progress);
};

AnimationUsingKeyFrames.Instance.AddKeyFrame = function (frame) {
    this.KeyFrames.Add(frame);
};
AnimationUsingKeyFrames.Instance.RemoveKeyFrame = function (frame) {
    this.KeyFrames.Remove(frame);
};

Nullstone.FinishCreate(AnimationUsingKeyFrames);
//#endregion<commit_msg>Implemented duration retrieval for key frames animations.<commit_after>/// <reference path="Animation.js"/>
/// CODE

//#region AnimationUsingKeyFrames
var AnimationUsingKeyFrames = Nullstone.Create("AnimationUsingKeyFrames", Animation);

AnimationUsingKeyFrames.Instance.Init = function () {
    this.Init$Animation();
};

//#region Properties

Nullstone.AbstractProperty(AnimationUsingKeyFrames, "KeyFrames");

//#endregion

AnimationUsingKeyFrames.Instance.Resolve = function (target, propd) {
    /// <param name="target" type="DependencyObject"></param>
    /// <param name="propd" type="DependencyProperty"></param>
    var keyFrames = this.KeyFrames;

    KeyFrameCollection.ResolveKeyFrames(this, keyFrames);

    var count = keyFrames._SortedList.length;
    for (var j = 0; j < count; j++) {
        if (!keyFrames._SortedList[j].KeyTime.IsValid)
            return false;
    }

    return true;
};
AnimationUsingKeyFrames.Instance.GetCurrentValue = function (defaultOriginValue, defaultDestinationValue, clockData) {
    var keyFrames = this.KeyFrames;

    var prevFrameRef = {};
    var currentKeyFrame = keyFrames.GetKeyFrameForTime(clockData.CurrentTime, prevFrameRef);
    var prevFrame = prevFrameRef.Value;
    if (currentKeyFrame == null)
        return null;

    var baseValue;
    var keyStartTime;
    var keyEndTime = currentKeyFrame._ResolvedKeyTime;
    if (prevFrame == null) {
        // the first keyframe, start at the animation's base value
        baseValue = defaultOriginValue;
        keyStartTime = new TimeSpan();
    } else {
        // start at the previous keyframe's target value
        baseValue = prevFrame.ConvertedValue;
        keyStartTime = prevFrame._ResolvedKeyTime;
    }

    var progress;
    if (clockData.CurrentTime.CompareTo(keyEndTime) >= 0) {
        progress = 1.0;
    } else {
        var keyDuration = keyEndTime._Ticks - keyStartTime._Ticks;
        if (keyDuration <= 0)
            progress = 1.0;
        else
            progress = (clockData.CurrentTime._Ticks - keyStartTime._Ticks) / keyDuration;
    }

    return currentKeyFrame.InterpolateValue(baseValue, progress);
};
AnimationUsingKeyFrames.Instance.GetNaturalDurationCore = function () {
    var keyFrames = this.KeyFrames;
    KeyFrameCollection.ResolveKeyFrames(this, keyFrames);
    var count = keyFrames._SortedList.length;
    if (count > 0)
        return keyFrames._SortedList[count - 1]._ResolvedKeyTime;
    return new Duration(new TimeSpan());
};

AnimationUsingKeyFrames.Instance.AddKeyFrame = function (frame) {
    this.KeyFrames.Add(frame);
};
AnimationUsingKeyFrames.Instance.RemoveKeyFrame = function (frame) {
    this.KeyFrames.Remove(frame);
};

Nullstone.FinishCreate(AnimationUsingKeyFrames);
//#endregion<|endoftext|>


<commit_before>var port = 8080;
var serverIP   = 'localhost';
var dbIP       = 'ec2-54-200-0-50.us-west-2.compute.amazonaws.com';
var dbPort     = '27017';

module.exports = {
	port: port,
	serverIP: serverIP,
	db: "mongodb://"+dbIP+":"+dbPort+"/picApp"
}<commit_msg>Pushing database fix<commit_after>var port = 8080;
var serverIP   = 'localhost';
var dbIP       = '54.200.0.50';
var dbPort     = '27017';

module.exports = {
	port: port,
	serverIP: serverIP,
	db: "mongodb://"+dbIP+":"+dbPort+"/picApp"
}<|endoftext|>