emjack

some crap you may find useful

Tính đến 09-05-2016. Xem phiên bản mới nhất.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name			emjack
// @version			4.3
// @description		some crap you may find useful
// @match			https://epicmafia.com/game/*
// @namespace https://greasyfork.org/users/4723
// ==/UserScript==

// welcome back
function emjack() {

	// yadayada
	var	alive=true,
		afk=false,
		meet="",
		master="",
		autobomb="",
		roulette=0,
		kicktimer=0,
		keys=0,
		auth=false,
		notes=null,
		users={};
	var	ANTIDC=0x0001,
		AUKICK=0x0002,
		AUWILL=0x0004,
		AUBOMB=0x0008,
		OBEYME=0x0010,
		UNOTES=0x0020,
		DEVLOG=0x0040,
		JEEVES=0x0080;
	var	K_DEBUG=0x0004;

	// public
	window.ej={
		name: "emjack",
		version: 4.3,
		cmds: {},
		notes: localStorage.notes
			? JSON.parse(localStorage.notes)
			: {},
		users: users,
		settings: +localStorage.ejs || AUKICK | AUWILL | UNOTES,
		};
	notes=ej.notes;
	afk=(ej.settings & JEEVES)===JEEVES;

	// plug in
	var	sock={ socket: null },
		postjackl=[];
	function postjack() {
		var	args=[];
		for(var i=0; i<arguments.length-1; i++) {
			args[i]=arguments[i];
			}
		postjackl.push(args, arguments[i]);
		};
	WebSocket.prototype.send=function(initial) {
		return function() {
			if(this!==sock.socket) {
				sock.build(this);
				}
			if(ej.settings & 0x0100) {
				arguments[0]=sock.intercept(arguments[0]);
				initial.apply(this, arguments);
				}
			else {
				(WebSocket.prototype.send=initial).apply(this, arguments);
				}
			};
		}(WebSocket.prototype.send);
	sock.build=function(socket) {
		this.socket=socket;
		if(window._emotes) {
			var	emotes=Object.keys(_emotes);
			log("", "pull-right emote emote-"+
				_emotes[emotes[Math.random()*emotes.length|0]]
				);
			}
		log(ej.name+ej.version+" connected", "pull-right");
		log((ej.settings|65536).toString(2).substring(1));
		socket.onmessage=function(initial) {
			return function(event) {
				sock.handle(event.data);
				if(alive) {
					initial.apply(this, arguments);
					setTimeout(function() {
						while(postjackl.length) {
							postjackl.pop().apply(null, postjackl.pop());
							}
						});
					}
				};
			}(socket.onmessage);
		};

	// socket
	sock.handle=function(data) {
		try {
			data=JSON.parse(data);
			}
		catch(error) {
			data=null;
			}
		if(data) for(var i=0, real=null; i<data.length; i++) {
			real=sock.parseShort(data[i][0], data[i][1]);
			if(ej.settings & DEVLOG) {
				console.log(" > %s:", real[0], real[1]);
				}
			if(ej.cmds[real[0]]) {
				ej.cmds[real[0]].call(ej, real[1]);
				}
			}
		};
	sock.intercept=function(data) {
		if(data[0]==="[") {
			try {
				data=JSON.parse(data);
				}
			catch(error) {
				return data;
				}
			if(ej.settings & DEVLOG) {
				console.log(" < %s:", data[0], data[1]);
				}
			if(ej.cmdi[data[0]]) {
				return JSON.stringify([data[0],
					ej.cmdi[data[0]](data[1])
					]);
				}
			return JSON.stringify(data);
			}
		return data;
		}
	sock.parseShort=function(cmd, data) {
		var	rfmt=this.short[cmd];
		if(rfmt) {
			if(data) for(key in rfmt.data) {
				data[key]=data[rfmt.data[key]] || data[key];
				// delete data[rfmt.data[key]];
				}
			return [rfmt.cmd, data];
			}
		else {
			return [cmd, data];
			}
		};
	sock.short=function(short) {
		var	rfmt={};
		for(i=0, data=null; i<short.length; i++) {
			data=short[i];
			rfmt[data.alias || data.cmd]={
				cmd: data.cmd || data.alias,
				data: data.data
				};
			}
		return rfmt;
		}(window.shorten);
	sock.cmd=function(cmd, data) {
		if(sock.socket) {
			sock.socket.send(
				JSON.stringify([cmd, data])
				);
			}
		};
	sock.chat=function(message, data) {
		if(typeof data==="object") {
			data.msg=message;
			data.meet=data.meet || meet;
			sock.cmd("<", data);
			}
		else sock.cmd("<", {
			meet: meet,
			msg: data ? "@"+data+" "+message : message
			});
		};
	sock.vote=function(vote, meeting) {
		sock.cmd("point", {
			meet: meeting || meet,
			target: vote
			});
		};
	sock.dcthen=function(callback) {
		alive=false;
		ej.redirect_back=callback;
		sock.cmd("leave");
		};

	// packets
	ej.cmdi={
		"$MODIFY": function(data) {
			for(key in data) {
				data[key]=prompt(key, data[key]);
				}
			return data;
			},
		"join": function(data) {
			if(keys & K_DEBUG) {
				keys^=K_DEBUG;
				return ej.cmdi.$MODIFY(data);
				}
			return data;
			}
		};
	ej.cmds={
		"auth": function(data) {
			var	ofg=document.querySelector("#option_fastgame"),
				ons=document.querySelector("#option_nospectate");
			if(ofg && !ofg.classList.contains("sel")) {
				ofg.classList.add("sel");
				sock.cmd("option", {
					field: "fastgame"
					});
				}
			if(ons && !ons.classList.contains("sel")) {
				ons.classList.add("sel");
				sock.cmd("option", {
					field: "nospectate"
					});
				}
			postjack(data, function(data) {
				auth=true;
				ku.send(0, Math.round(ej.version*10-42));
				});
			},
		"round": function(data) {
			for(var x in users) {
				users[x].voted=false;
				}
			if(auth && data.state===1 && ej.settings & AUWILL) {
				postjack(data, function(data) {
					log("Wrote will.", "lastwill");
					sock.cmd("will", {
						msg: user+"."+u(user).role
						});
					});
				}
			},
		"users": function(data) {
			for(var x in data.users) {
				u.make(data.users[x]);
				}
			setTimeout(function() {
				var	node;
				for(x in data.users) {
					node=document.querySelector("[data-uname="+x+"]");
					if(node) {
						node.setAttribute("title", notes[x] || "no notes for "+x);
						}
					}
				}, 2000);
			},
		"left": function(data) {
			for(var i=0; i<data.left.length; i++) {
				u(data.left[i]).dead=true;
				}
			},
		"anonymous_players": function(data) {
			for(var x in users) {
				delete users[x];
				}
			for(var i=0; i<data.players.length; i++) {
				u.make(data.players[i]);
				}
			},
		"anonymous_reveal": function(data) {
			if(data.user===user) {
				u.make(u(this.mask));
				}
			},
		"join": function(data) {
			u.make(data.data);
			log(data.user+" has joined");
			if(ej.settings & AUKICK && /autokick/.test(notes[data.user])) {
				sock.cmd("ban", {
					uid: data.id
					});
				}
			else {
				var	user=this.user;
				requestAnimationFrame(function() {
					$(".user_li.ng-scope[data-uname="+user+"]")
						.attr("title", notes[user]||"no notes for "+user);
					});
				}
			},
		"leave": function(data) {
			if(!data.user) {
				data.user=data.u;
				}
			log(data.user+" has left");
			delete users[data.user];
			},
		"kick": function(data) {
			u(data.user).dead=true;
			},
		"kill": function(data) {
			u(data.target).dead=true;
			},
		"k": function(data) {
			ku.recv(u(data.user || data.u), 1, Date.now());
			},
		"u": function(data) {
			ku.recv(u(data.user || data.u), 0, Date.now());
			},
		"<": function(data) {
			if(auth && ej.settings & OBEYME) {
				if(data.msg[0]==="@" && !ranked) {
					var	target=data.msg.replace(/@(\w+).+/, "$1"),
						message=data.msg.replace(/@\w+ (.+)/, "$1");
					if(target===user) {
						ej.run(message, ej.lbot, data);
						}
					}
				}
			},
		"speech": function(data) {
			if(data.type==="contact") {
				postjack(data, function(data) {
					log("The roles are... "+data.data.join(", "));
					});
				}
			},
		"meet": function(data) {
			if(data.say) {
				meet=data.meet;
				for(var i=0; i<data.members.length; i++) {
					u(data.members[i]).meet=data.meet;
					}
				}
			if(ej.settings & 0x0100) {
				for(var x in data.disguise) {
					postjack(data, name, function(data, name) {
						log(name+" is "+data.disguise[name]);
						});
					}
				}
			switch(data.meet) {
				case "mafia":
					if(auth && !data.disguise && !ranked) {
						if(ej.settings & OBEYME && false) {
							postjack(user, function(data) {
								sock.chat(u(data).role, {
									meet: "mafia"
									});
								});
							}
						}
				case "thief":
					u(user).mafia=true;
					for(var x in users) {
						if(!data.choosedata[x] && !u(x).dead) {
							u(x).mafia=true;
							postjack(x, function(data) {
								log(data+" is your partner!");
								document.querySelector("[data-uname="+data+"] .username")
									.textContent=data+" (partner)";
								});
							}
						}
					break;
				}
			if(afk) {
				sock.vote(data.basket[0], data.meet);
				}
			},
		"end_meet": function(data) {
			if(data.say) {
				for(var x in users) {
					if(users[x].meet===data.meet) {
						users[x].meet=null;
						}
					}
				}
			},
		"point": function(data) {
			u(data.user).voted=!data.unpoint;
			},
		"reveal": function(data) {
			u(data.user).role=data.data;
			if(!u(data.user).dead) {
				if(data.user===user) {
					postjack(data, function(data) {
						log(data.user===user
							? "Your role is now "+data.data
							: data.user+" is a "+data.data
							);
						});
					}
				}
			},
		"countdown": function(data) {
			clearTimeout(kicktimer);
			kicktimer=setTimeout(function() {
				sock.cmd("kick");
				}, data.totaltime);
			},
		"start_input": function(data) {
			if(afk || ej.settings & AUBOMB) {
				postjack(data, Object.keys(users), function(data, names) {
					sock.cmd("input", {
						id: data.id,
						input: {
							player: names[Math.random()*names.length|0]
							}
						});
					});
				}
			},
		"redirect": function(data) {
			if(!alive) {
				ej.redirect_back();
				}
			}
		};

	// kucode
	var	ku={};
	ku.send=function(op, code) {
		code+=op<<6;
		if(ej.settings & DEVLOG) {
			log(" * "+user+": "+(code|1024).toString(2));
			}
		setTimeout(function() {
			for(var i=9; i>=0; i--) {
				sock.cmd(code>>i & 1 ? "k" : "u");
				}
			}, 200);
		};
	ku.recv=function(u, bit, time) {
		if(time-u.kuclock > 100) {
			u.kucode=1;
			u.kuclock=Infinity;
			}
		else {
			u.kucode<<=1;
			u.kucode|=bit;
			if(u.kucode & 1024) {
				if(ej.settings & DEVLOG) {
					log(" * "+u.name+": "+u.kucode.toString(2));
					}
				if(ku.op[u.kucode>>6 & 15]) {
					ku.op[u.kucode>>6 & 15]
						(u, u.kucode & 63);
					}
				u.kucode=1;
				u.kuclock=Infinity;
				}
			else {
				u.kuclock=time;
				}
			}
		};
	ku.op=[
		function(u, code) {
			if(!u.emjack) {
				u.emjack=(42+code)/10;
				ku.send(0, Math.round(ej.version*10-42));
				}
			},
		null,
		function(u, code) {
			log(u.name+" sent "
				+(code|64).toString(2).substring(1)
				+":"+code.toString()
				+":"+String.fromCharCode(code+96)
				);
			},
		function(u, code) {
			log("You've been poked!");
			}
		];

	// chat base
	ej.run=function(input, list, data) {
		for(var i=0, match=null; i<list.length; i++) {
			match=list[i].regex.exec(input);
			if(match!==null && input===match[0]) {
				data ? match[0]=data : match.shift();
				list[i].callback.apply(list[i], match);
				break;
				}
			}
		};

	// chat commands
	ej.lchat=[
		{
			name: "Scriptcheck",
			regex: /sc|scriptcheck/,
			callback: function() {
				log(ej.name+ej.version);
				}
			},
		{
			name: "EM commands",
			regex: /(me .+)/,
			callback: function(msg) {
				sock.chat("/"+msg);
				}
			},
		{
			name: "About",
			regex: /info|help|about/,
			callback: function() {
				log(ej.name+ej.version, "bold");
				log("Type /cmdlist for a list of commands");
				}
			},
		{
			name: "Evaluate",
			regex: /eval (.+)/,
			callback: function(input) {
				log(JSON.stringify(eval(input)) || "undefined");
				}
			},
		{
			name: "Clear chat/logs",
			regex: /clear( logs)?/,
			callback: function(_logs) {
				var	nodelist=_logs
					? document.querySelectorAll("#window .emjack")
					: chat.children;
				for(var i=0; i<nodelist.length; i++) {
					nodelist[i].parentElement.removeChild(nodelist[i]);
					}
				}
			},
		{
			name: "Get metadata",
			regex: /meta(?:data)?/,
			callback: function() {
				for(var param in ej.meta) {
					log("@"+param+": "+ej.meta[param]);
					}
				}
			},
		{
			name: "Get whois",
			regex: /whois (.+)/,
			callback: function(name) {
				if(users[name]) {
					log(users[name].name+" ("+users[name].id+") "+(
						users[name].emjack ? "ej"+users[name].emjack : ""
						), "bold");
					log("emotes: "+(
						users[name].emotes
							? Object.keys(users[name].emotes).join(" ") || "none found"
							: "does not own"
						));
					}
				else {
					log("Can't find '"+name+"'");
					}
				}
			},
		{
			name: "Get emotes",
			regex: /emotes/,
			callback: function() {
				log("Sitewide emotes", "bold");
				log(Object.keys(_emotes || {}).join(" ") || "none found");
				log("Lobby emotes", "bold");
				log(Object.keys(lobby_emotes || {}).join(" ") || "none found");
				}
			},
		{
			name: "Get command list",
			regex: /cmdlist ?(bot)?/,
			callback: function(_bot) {
				var	data=(_bot ? ej.lbot : ej.lchat);
				for(var i=0; i<data.length; i++) {
					log(data[i].name+" ~ "+data[i].regex);
					}
				}
			},
		{
			name: "Toggle afk",
			regex: /afk( on)?/,
			callback: function(_on) {
				if(_on) {
					ej.settings^=JEEVES;
					afk=(ej.settings & JEEVES)===JEEVES;
					}
				else {
					afk=!afk;
					}
				log(afk
					? "Jeeves will handle your affairs."
					: "Jeeves has been dismissed."
					);
				}
			},
		{
			name: "Toggle autowill",
			regex: /aw|autowill/,
			callback: function() {
				ej.settings^=AUWILL;
				log(ej.settings & AUWILL
					? "Name & role will be written in will by default."
					: "Disabled autowill."
					);
				}
			},
		{
			name: "Toggle autokick",
			regex: /ak|autokick/,
			callback: function() {
				ej.settings^=AUKICK;
				log(ej.settings & AUKICK
					? "Note-based autokick enabled."
					: "Disabled autokick."
					);
				}
			},
		{
			name: "Toggle dev logs",
			regex: /dev/,
			callback: function() {
				ej.settings^=DEVLOG;
				log(ej.settings & DEVLOG
					? "Logging debug data."
					: "Logging disabled."
					);
				}
			},
		{
			name: "Toggle slavemode",
			regex: /slave/,
			callback: function() {
				ej.settings^=OBEYME;
				log(ej.settings & OBEYME
					? "You're a naughty girl."
					: "You found Jesus."
					);
				}
			},
		{
			name: "Toggle roulette",
			regex: /roulette/,
			callback: function() {
				roulette=roulette?0:6;
				if(roulette) {
					sock.chat("Reloaded the revolver. Who's next?");
					}
				}
			},
		{
			name: "Poke",
			regex: /poke/,
			callback: function() {
				ku.send(3, 0);
				}
			},
		{
			name: "Ping",
			regex: /ping ?(.+)?/,
			callback: function(code) {
				ku.send(2, code
					? parseInt(code, 2) || +code || code.charCodeAt(0)-96
					: 0);
				}
			},
		{
			name: "Send message",
			regex: /say (\w+)/,
			callback: function(msg) {
				sock.chat(msg);
				}
			},
		{
			name: "Send whisper",
			regex: /w (\w+) ?(\w+)?/,
			callback: function(to, msg) {
				sock.chat(msg, {
					whisper: true,
					target: user
					});
				}
			},
		{
			name: "Send ping",
			regex: /ping ?(all)?/,
			callback: function(all) {
				var	pingees=[];
				for(var x in users) {
					if(!users[x].dead && !users[x].voted && users[x].meet===meet) {
						pingees.push(x);
						}
					}
				sock.chat(pingees.join(" "));
				}
			},
		{
			name: "Send kick",
			regex: /kick (\w+)/,
			callback: function(name) {
				sock.cmd("ban", {
					uid: u(name).id
					});
				}
			},
		{
			name: "Send vote",
			regex: /vote ?(no one)?(\w+)?/,
			callback: function(none, name) {
				sock.vote(
					none!==undefined?
						"*":
						name!==undefined?
							name:
							user
							);
				}
			},
		{
			name: "Send vote (gun)",
			regex: /shoot (\w+)/,
			callback: function(name) {
				sock.vote(name, "gun");
				}
			},
		{
			name: "Lobby host",
			regex: /host ?(.+)?/,
			callback: function(title) {
				sock.dcthen(function() {
					$.getJSON("/game/add/mafia", {
						setupid: setup_id,
						ranked: false,
						add_title: title===undefined?0:1,
						game_title: title
						}, function(json) {
							location.href="/game/"+json.table;
							});
					});
				}
			},
		{
			name: "Lobby games",
			regex: /games/,
			callback: function() {
				$.ajax({
					url: "/game/find?page=1",
					method: "get"
					}).success(function(json) {
						var	a, div, games=JSON.parse(json[1]);
						games.data.forEach(function(table) {
							if(table.status_id!==0 || table.password!==false) {
								return;
								}
							a=document.createElement("a");
							a.textContent="Table "+table.id;
							a.addEventListener("click", function(event) {
								sock.dcthen(function() {
									location.href="/game/"+table.id;
									});
								});
							div=document.createElement("div");
							div.appendChild(a);
							div.appendChild(
								document.createTextNode(" - "+table.numplayers+" / "+table.target+" players")
								);
							if(table.id===game_id) {
								div.appendChild(
									document.createTextNode(" (you)")
									);
								}
							log(div);
							});
						});
				}
			},
		{
			name: "[Naughty] Will",
			regex: /will (.+)/,
			callback: function(will) {
				if(ranked) {
					log("Disabled in ranked games.");
					}
				else if(ej.settings & 0x0100) {
					log("You revised your will.", "lastwill");
					sock.cmd("will", {
						msg: will
						});
					}
				}
			},
		{
			name: "[Naughty] Dethulu",
			regex: /(?:dt|thulu) (.+)/,
			callback: function(message) {
				if(ranked) {
					log("Disabled in ranked games.");
					}
				else if(true || ej.settings & 0x0100) {
					sock.cmd("<", {
						meet: meet,
						msg: "\u200B",
						quote: true,
						target: message
						});
					}
				}
			},
		{
			name: "[Naughty] Fakequote",
			regex: /(?:fq|quote) (\w+) (.+)/,
			callback: function(who, message) {
				if(ranked) {
					log("Disabled in ranked games.");
					}
				else if(true || ej.settings & 0x0100) {
					sock.cmd("<", {
						meet: meet,
						msg: message,
						quote: true,
						target: who
						});
					}
				}
			},
		{
			name: "[Naughty] Autobomb",
			regex: /(?:ab|autobomb) ?(\w+)?/,
			callback: function(name) {
				if(ranked) {
					log("Disabled in ranked games.");
					}
				else if(ej.settings & 0x0100) {
					if(name) {
						autobomb=name;
						ej.settings|=AUBOMB;
						log("Passing the bomb to "+name);
						}
					else {
						autobomb="";
						ej.settings^=AUBOMB;
						log(ej.settings & AUBOMB
							? "You're now an anarchist!"
							: "You're now a tree."
							);
						}
					}
				}
			},
		{
			name: "[Naughty] Fake Sysmessage",
			regex: /f(s)? ?(\w+)? ?(.+)?/,
			callback: function(send, id, input) {
				if(ranked) {
					log("Disabled in ranked games.");
					}
				else if(true || ej.settings & 0x0100) {
					var	output=this.messages[id];
					if(!output) {
						log("System messages: "+Object.keys(this.messages).join(", "));
						}
					else {
						input=input ? input.split(" ") : output.default;
						(send ? sock.chat : log)(output.msg.replace(/\%(\d+)/g, function(match, i) {
							return input[+i]
								? +i+1===output.default.length
									? input.slice(i).join(" ")
									: input[+i]
								: output.default[+i];
							}));
						}
					}
				},
			messages: {
				angel: {
					msg: "You feel an overwhelming, unconditional love for %0. "
						+"You feel you must protect %0 with your life.",
					default: [user]
					},
				auto: {
					msg: "There might be an autocrat among you...",
					default: []
					},
				bleed: {
					msg: "You start to bleed...",
					default: []
					},
				bomb: {
					msg: "%0 rushes at %1 and reveals a bomb!",
					default: [user, user]
					},
				carol: {
					msg: "You see a merry Caroler outside your house! "
						+"They sing you a Carol about %0, %1, %2. At least one of which is the Mafia!",
					default: [user, user, user]
					},
				chef: {
					msg: "You find yourself in a dimly lit banquet! "
						+"You sense the presence of a masked guest. The guest appears to be a %0.",
					default: ["ninja"]
					},
				cm: {
					msg: "You glance at your watch. The time is now %0 o'clock.",
					default: ["11"]
					},
				cmlife: {
					msg: "Your watch whispers to you. You have one extra life.",
					default: []
					},
				confess: {
					msg: "At the confessional tonight, a %0 had visited you to confess their sins.",
					default: ["survivor"]
					},
				cop: {
					msg: "After investigations, you suspect that %0 is sided with the %1.",
					default: [user, "mafia"]
					},
				cry: {
					msg: "Someone cries out | %0",
					default: [""]
					},
				det: {
					msg: "Through your detective work, you learned that %0 is a %1!",
					default: [user, "ninja"]
					},
				disc: {
					msg: "You discover that %0 is the %1!",
					default: [user, "interceptor"]
					},
				dream: {
					msg: "You had a dream... where at least one of %0, %1, %2 is a mafia...",
					default: [user, user, user]
					},
				fire: {
					msg: "Somebody threw a match into the crowd! "+
						"%0 suddenly lights on fire and burns to a crisp!",
					default: [user]
					},
				firefail: {
					msg: "Somebody threw a match into the crowd!",
					default: []
					},
				guise: {
					msg: "You are now disguised as %0.",
					default: [user]
					},
				guised: {
					msg: "%0 has stolen your identity!",
					default: [user]
					},
				gun: {
					msg: "You hear a gunshot!",
					default: []
					},
				gunfail: {
					msg: "%0 reveals a gun! The gun backfires!",
					default: [user]
					},
				gunhit: {
					msg: "%0 reveals a gun and shoots %0!",
					default: [user, user]
					},
				hit: {
					msg: "A bullet hits your vest! You cannot survive another hit!",
					default: []
					},
				invis: {
					msg: "Someone whispers %0",
					default: [""]
					},
				item: {
					msg: "You received a %0!",
					default: ["key"]
					},
				jail: {
					msg: "You have been blindfolded and sent to jail!",
					default: []
					},
				jan: {
					msg: "While cleaning up the mess, you learned that %0 was a %1.",
					default: [user, "cop"]
					},
				janday: {
					msg: "%0 is missing!",
					default: [user]
					},
				learn: {
					msg: "You learn that %0 is a %1",
					default: [user, "cop"]
					},
				lm: {
					msg: "A loud voice was heard during the night: \"Curses! %0 woke me from my slumber!\"",
					default: [user]
					},
				lonely: {
					msg: "You spent a silent and lonely night at church. No one came to visit you.",
					default: []
					},
				love: {
					msg: "During the night, you fall in love with %0 after a romantic conversation!",
					default: [user]
					},
				lynch: {
					msg: "You feel very irritated by %0.",
					default: [user]
					},
				matin: {
					msg: "Penguins be matin'",
					default: []
					},
				message: {
					msg: "You received a message: %0",
					default: [""]
					},
				mfail: {
					msg: "No matter how much you worked your magic, %0 and %1 refuses to fall in love!",
					default: [user, user]
					},
				mlove: {
					msg: "You cast a Christmas spell on %0 and %1... they are now in love!",
					default: [user, user]
					},
				mm: {
					msg: "There might be a mastermind among you...",
					default: []
					},
				mort: {
					msg: "You learned that %0 is a %1!",
					default: [user, "villager"]
					},
				party: {
					msg: "You find yourself at a vibrant party!",
					default: []
					},
				pengi: {
					msg: "During the night a fluffy penguin visits you and tells you that "+
						"%0 is carrying a %1.",
					default: [user, user]
					},
				pengno: {
					msg: "During the night a fluffy penguin visits you and tells you that "+
						"%0 has taken no action over the course of the night.",
					default: [user]
					},
				poison: {
					msg: "You feel sick, as though you had been poisoned!",
					default: []
					},
				pop: {
					msg: "%0 feels immensely frustrated!",
					default: [user]
					},
				psy: {
					msg: "You read %0's mind... they are thinking %1 thoughts.",
					default: [user, "evil"]
					},
				psyfail: {
					msg: "You tried to read %0's mind, but something distracted you.",
					default: [user]
					},
				pvisit: {
					msg: "During the night a fluffy penguin visits you and tells you that "+
						"%0 visited %1.",
					default: [user, "no one"]
					},
				pevisited: {
					msg: "During the night a fluffy penguin visits you and tells you that "+
						"%0 was visited by %1.",
					default: [user, "no one"]
					},
				santa: {
					msg: "After going out on your sleigh, you find that %0 is %1!",
					default: [user, "neither naughty nor nice"]
					},
				snoop: {
					msg: "After some snooping, you find out %0 is carrying %2 %1.",
					default: [user, "gun", "1"]
					},
				snoop0: {
					msg: "After some snooping, you find out %0 is not carrying any items..",
					default: [user]
					},
				stalk: {
					msg: "Through stalking, you learned that X is a R!",
					default: [user, "cop"]
					},
				thulu: {
					msg: "You were witness to an unimaginable evil... you cannot forget... "
						+"your mind descends into eternal hell.",
					default: []
					},
				track: {
					msg: "You followed %0 throughout the night. %0 visited %1.",
					default: [user, "no one"]
					},
				track: {
					msg: "You became a tree!",
					default: []
					},
				trust: {
					msg: "You had a dream... you learned you can trust %0...",
					default: [user]
					},
				virgin: {
					msg: "The virgin has been sacrified!",
					default: []
					},
				voodoo: {
					msg: "%0 suddenly feels a chill and falls to the ground!",
					default: [user]
					},
				watch: {
					msg: "You watched %0 throughout the night. %1 has visited %0.",
					default: [user, "No one"]
					},
				will: {
					msg: "You read the will of %0, it reads: %1",
					default: [user, ""]
					},
				ww: {
					msg: "You devoured a human and feel very powerful... "
						+"as though you are immortal for the day!",
					default: []
					}
				}
			}
		];

	// this is a sin
	ej.lbot=[
		{
			name: "Scriptcheck",
			regex: /sc|scriptcheck/,
			callback: function(data) {
				sock.chat(ej.name+ej.version, data.user);
				}
			},
		{
			name: "Echo",
			regex: /(?:echo|say) (.+)/,
			callback: function(data, what) {
				// sock.chat(what);
				}
			},
		{
			name: "Do Command",
			regex: /docmd (.+)/,
			callback: function(data, what) {
				ej.run(what, ej.lchat);
				}
			},
		{
			name: "Say hi",
			regex: /hi+|hey+|hel+o+/,
			callback: function(data) {
				sock.chat(
					this.responses[Math.random()*this.responses.length|0],
					data.user
					);
				},
			responses: [
				"hi...", "h-hi", "hey..."
				]
			},
		{
			name: "Say hi 2",
			regex: /(?:wh?at'?)s ?up+/,
			callback: function(data) {
				sock.chat(
					this.responses[Math.random()*this.responses.length|0],
					data.user
					);
				},
			responses: [
				"hi...", "h-hi", "hey...",
				"nothing", "something", "everything", "im dead inside"
				]
			},
		{
			name: "Notice me",
			regex: /\<3|i (?:like|love) you|(?:ur|you?'re?) cute/,
			callback: function(data) {
				sock.chat(
					this.responses[Math.random()*this.responses.length|0]
					);
				},
			responses: [
				"o-oh...", "/me blushes", "/me blushes..."
				]
			},
		{
			name: "Roulette",
			regex: /roulette/,
			callback: function(data) {
				if(roulette) {
					var	user=data.user,
						message1=this.message1[Math.random()*this.message1.length|0],
						message2=this.message2[Math.random()*this.message2.length|0],
						message3=this.message3[Math.random()*this.message3.length|0];
					sock.chat(roulette+" chambers left. You put the gun to your head...", user);
					setTimeout(function() {
						if(Math.random()*roulette>1) {
							roulette--;
							sock.chat(message1+", "+message2+", and nothing happens.", user);
							}
						else {
							roulette=0;
							sock.chat(message1+", "+message2+", and "+message3+".", user);
							sock.vote(user, "gun");
							}
						}, 3000);
					}
				},
			message1: [
				"Wet your pants", "Gulp", "Get ready", "Say your prayers"
				],
			message2: [
				"pull it", "pull the trigger", "let it rip", "gently tug"
				],
			message3: [
				"die instantly", "kiss the bullet", "meet Jesus"
				]
			},
		{
			name: "Bomb fight",
			regex: /fight ?me/,
			callback: function(data) {
				autobomb=data.user;
				sock.chat("ok", data.user);
				}
			},
		{
			name: "Obey",
			regex: /obey me|be my bi?t?ch/,
			callback: function(data) {
				if(!master) {
					master=data.user;
					sock.chat("yes master...", data.user);
					}
				else {
					sock.chat("i belong to "+master, data.user);
					}
				}
			},
		{
			name: "Roll over",
			regex: /roll ?over/,
			callback: function(data) {
				sock.chat(data.user===master
					? "/me rolls over for senpai"
					: "you're not the boss of me"
					);
				}
			},
		{
			name: "Bow",
			regex: /bow(?: ?down)?/,
			callback: function(data) {
				sock.chat(data.user===master
					? "/me bows for her master..."
					: "you're not the boss of me"
					);
				}
			},
		{
			name: "Beg",
			regex: /beg(?: for (.+))?/,
			callback: function(data, what) {
				if(data.user===master) {
					var	output=this.responses[Math.random()*this.responses.length|0];
					if(what) {
						output+=" for "+what;
						}
					else if(Math.random()>0.5) {
						output+=" master";
						}
					sock.chat(output);
					}
				else {
					sock.chat("blow me");
					}
				},
			responses: [
				"/me gets on her knees and begs",
				"/me gets on all fours and begs"
				]
			},
		{
			name: "Vote",
			regex: /vote (\w+)/,
			callback: function(data, who) {
				if(data.user===master) {
					sock.vote(who, data.meet);
					}
				}
			},
		{
			name: "Shoot",
			regex: /shoot (\w+)/,
			callback: function(data, who) {
				if(data.user===master) {
					sock.vote(who, "gun");
					}
				}
			},
		{
			name: "Claim",
			regex: /claim(?: to me)?/,
			callback: function(data) {
				if(data.user===master) {
					sock.chat(u(user).role+"...", {
						whisper: true,
						target: data.user
						});
					}
				}
			}
		];

	// utility
	function u(name) {
		return users[name || user] || u.make({
			id: 0,
			username: name || user
			});
		};
	u.make=function(data) {
		data.name=data.username || data.user;
		data.emjack=0;
		data.role=null;
		data.meet=meet;
		data.mafia=false;
		data.dead=false;
		data.voted=false;
		data.kucode=1;
		data.kuclock=Infinity;
		users[data.name]=data;
		if(data.emotes) {
			data.emotes=JSON.parse(data.emotes);
			}
		return data;
		};
	function log(message, classes) {
		var	node=document.createElement("div");
		node.className=classes ? "log emjack "+classes : "log emjack";
		typeof message==="string"
			? node.textContent=message
			: node.appendChild(message);
		if(chat.scrollTop>=chat.scrollHeight-chat.clientHeight) {
			requestAnimationFrame(function() {
				chat.scrollTop=chat.scrollHeight;
				});
			}
		chat.appendChild(node);
		};

	// keep chat
	document.querySelector("#speak_container")
		.style.cssText="display: initial !important";
	
	// townie input
	var	chat=document.querySelector("#window"),
		typebox=document.querySelector("#typebox"),
		notebox=document.querySelector("textarea.notes");
	typebox.addEventListener("keydown", function(event) {
		if(event.which===13 && this.value[0]==="/") {
			ej.run(this.value.substring(1), ej.lchat);
			this.value="";
			}
		});
	notebox.addEventListener("focus", function(event) {
		this.value=notes[document.querySelector(".user_header > h2").textContent];
		});
	notebox.addEventListener("keyup", function(event) {
		notes[document.querySelector(".user_header > h2").textContent]=this.value;
		});

	// clean up
	var	last_error=null;
	window.addEventListener("error", function(event) {
		var	message=event.error.message;
		if(message!==last_error) {
			log(message, "pull-right");
			log("You've got error!", "bold");
			last_error=message;
			}
		});
	window.addEventListener("beforeunload", function(event) {
		localStorage.ejs=ej.settings;
		if(ej.settings & UNOTES) {
			localStorage.notes=JSON.stringify(notes);
			}
		});
	window.addEventListener("keydown", function(event) {
		if(event.ctrlKey) {
			switch(event.which) {
				case 66:
					sock.cmd("option", {
						field: "fastgame"
						});
					sock.cmd("option", {
						field: "nospectate"
						});
					break;
				case 81:
					sock.cmd("<", {
						meet: meet,
						msg: typebox.value,
						quote: true,
						target: user
						});
					typebox.value="";
					break;
				}
			}
		else if(event.target.value===undefined) {
			if(event.which===192) {
				keys|=K_DEBUG;
				}
			typebox.focus();
			}
		});

	}

// syringe
function inject(fn) {
	var	script=document.createElement("script");
	script.type="text/javascript";
	script.textContent="("+String(fn)+")()";
	document.body.appendChild(script);
	}

// jack in
setTimeout(function() {
	inject(emjack);
	document.body.addEventListener("contextmenu", function(event) {
		event.stopPropagation();
		}, true);
	});