PlugPlug

For use on plug.dj

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name       PlugPlug
// @namespace  http://use.i.E.your.homepage/
// @version    0.1
// @description  For use on plug.dj
// @match      http://plug.dj/friendshipismagic/
// @copyright  2012+, Mateon
// ==/UserScript==
/**
 * PlugPlug - A script for plug.dj by Mateon1 ([email protected])
 * Planned:
 * - Song skiplist/banlist.
 */
//Remove button
function addDeleteButton(messageID) {
    $(".cid-" + messageID).append("<div class='delete-button' style='display: none;'><span>Delete</span></div>");
    $(".cid-" + messageID + "> .delete-button").click(function () {
        API.moderateDeleteChat(messageID);
    });
    $(".cid-" + messageID).mouseover(function () {
        $(".cid-" + messageID + "> .delete-button").css('display', 'block');
    });
    $(".cid-" + messageID).mouseleave(function () {
        $(".cid-" + messageID + "> .delete-button").css('display', 'none');
    });
}
 
function check(data) {
    if (API.getUser().permission < 2) {
        API.off(API.CHAT, check);
        return;
    }
    if (API.getUser(data.fromID.permission) < 2) {
        return;
    }
    if (API.getUser(data.fromID).permission >= API.getUser().permission) {
        addDeleteButton(data.chatID);
    }
}
API.on(API.CHAT, check);
API.chatLog('Loaded Remove Button')

//plugplug
var version = "0.70b",	// Bot version
	changed = [	"* Fixed chat not scrolling if an image was posted."],
	isOwner = API.getUser().id === "518a0d73877b92399575657b",
	canSkip = true,
	CSDelay = -1,
	lastVol = API.getVolume(),
	isMuted = !lastVol,
	artMute = false,
	chatBan,
	ppSaved,

// ALOT		\\			  //
			// alot of vars\
		 //(*)  ________"(*)\
		// : ' // . var\\ ,(I love them alot!)\
	   //  var//.   , ' \\  ;   .  Alots are cool
	   //  .  | : var  ; | MOAR ALOT, MOAR var!!!!
	   alotOfAlot = true, //A LOT OF A LOT OF ALOT
	   alotOfTimeout = null; // Contains an alot  \
	   //of timeout var . : alot '  " ,   \\.  ,   \
	   //alot|     var "  alot ,  ' var  timeout   \
	  //of leg      alot foot     "    *    much var.

function dump() {
	var r = "", tryGet;
	function testStorage() {
		var s = ppSaved, r = localStorage.PlugPlug, e = 0;
		e += s.hasOwnProperty("autojoin");
		e += s.hasOwnProperty("autowoot");
		e += s.hasOwnProperty("user2ID");
		e += s.hasOwnProperty("automuted");
		e += s.hasOwnProperty("stats");
		return e + "/5";
	}
	if (window.hasOwnProperty("ppSaved")) {
		tryGet = function (e) {
			if (ppSaved.hasOwnProperty(e)) {
				return ppSaved[e];
			}
			return "NULL";
		};
	} else {
		tryGet = function () {return "NULL"; };
	}
	r += localStorage.hasOwnProperty("PlugPlug") ? localStorage.PlugPlug.length + ":" + testStorage() : "NULL";
	r += "|" + tryGet("autojoin") + "|" + tryGet("autowoot");
	//...
	API.sendChat(r);
}

function mulStr(x, n) {
	var s = '';
	while (true) {
		if (n & 1) {s += x; }
		n >>= 1;
		if (n) {x += x; } else {break; }
	}
	return s;
}
/*
function terrariaDeath() {
	var result = "";
		text = "";
	switch (Math.floor(Math.random() * 26)) {
		text = " was slain";
		text = " was eviscerated";
		text = " was murdered";
		text = "'s face was torn off";
		text = "'s entrails were ripped out";
		text = " was destroyed";
		text = "'s skull was crushed";
		text = " got massacred";
		text = " got impaled";
		text = " was torn in half";
		text = " was decapitated";
		text = " let their arms get torn off";
		text = " watched their innards become outards";
		text = " was brutally dissected";
		text = "'s extremities were detached";
		text = "'s body was mangled";
		text = "'s vital organs were ruptured";
		text = " was turned into a pile of flesh";
		text = " was removed from " + worldName;
		text = " got snapped in half";
		text = " was cut down the middle";
		text = " was chopped up";
		text = "'s plead for death was answered";
		text = "'s meat was ripped off the bone";
		text = "'s flailing about was finally stopped";
		text = " had their head removed";
	}
	if (plr >= 0 && plr < 255)
	{
		if (proj >= 0 && Main.projectile[proj].name != "")
		{
			result = string.Concat(new string[]
			{
				text,
				" by ",
				Main.player[plr].name,
				"'s ",
				Main.projectile[proj].name,
				"."
			});
		}
		else
		{
			result = string.Concat(new string[]
			{
				text,
				" by ",
				Main.player[plr].name,
				"'s ",
				Main.player[plr].inventory[Main.player[plr].selectedItem].name,
				"."
			});
		}
	}
	else
	{
		if (npc >= 0 && Main.npc[npc].displayName != "")
		{
			result = text + " by " + Main.npc[npc].displayName + ".";
		}
		else
		{
			if (proj >= 0 && Main.projectile[proj].name != "")
			{
				result = text + " by " + Main.projectile[proj].name + ".";
			}
			else
			{
				if (other >= 0)
				{
					if (other == 0)
					{
						if (Main.rand.Next(2) == 0)
						{
							result = " fell to their death.";
						}
						else
						{
							result = " didn't bounce.";
						}
					}
					else
					{
						if (other == 1)
						{
							int num2 = Main.rand.Next(4);
							if (num2 == 0)
							{
								result = " forgot to breathe.";
							}
							else
							{
								if (num2 == 1)
								{
									result = " is sleeping with the fish.";
								}
								else
								{
									if (num2 == 2)
									{
										result = " drowned.";
									}
									else
									{
										if (num2 == 3)
										{
											result = " is shark food.";
										}
									}
								}
							}
						}
						else
						{
							if (other == 2)
							{
								int num3 = Main.rand.Next(4);
								if (num3 == 0)
								{
									result = " got melted.";
								}
								else
								{
									if (num3 == 1)
									{
										result = " was incinerated.";
									}
									else
									{
										if (num3 == 2)
										{
											result = " tried to swim in lava.";
										}
										else
										{
											if (num3 == 3)
											{
												result = " likes to play in magma.";
											}
										}
									}
								}
							}
							else
							{
								if (other == 3)
								{
									result = text + ".";
								}
							}
						}
					}
				}
			}
		}
	}
	return result;
}*/

function rnd(d, m, x, u, b, a) {
	//return [0]; //lael
	// a(PI key) and b(ase) unused/unimplemented (after a month, Random.org still hasn't sent an API key)
	var r = [], up = new TypeError();
	a = a === undefined ? "[INSERT API KEY HERE]" : a;
	b = b === undefined ? 10 : b;
	if ((u && x - m > d) || d < 0) {
		throw up;
	}
	x += 1; // Include max
	if (u) {
		r = ["u", "n", "i", "m", "p", "l", "e", "m", "e", "n", "t", "e", "d"];
	} else {
		while (d > 0) {
			d -= 1;
			r.push(Math.floor(Math.random() * (x - m) + m));
		}
	}
	return r;
}

function unmute() {
	var a = isMuted;
	if (isMuted) {
		artMute = isMuted = false;
		API.setVolume(lastVol);
	}
	return a;
}

function mute() {
	var a = isMuted;
	if (!isMuted) {
		lastVol = API.getVolume();
		artMute = isMuted = true;
		API.setVolume(0);
	}
	return !a;
}

/*
(function () {
	function e() {
		t = new XMLHttpRequest();
		t.open("GET", "http://plug.dj/friendshipismagic/");
		t.onerror = e;
		t.onload = function (a) {
			if (t.status == 500)
				return e();
			console.log("Plug is working :D\nRefreshing in 3 seconds");
			setTimeout(function () {
				location.reload()
			}, 3000)
		};
		t.send()
	}
	e()
})()
*/

function cycleEnabled() {return $(".cycle-toggle")[0].className.indexOf("enabled") !== -1; }
function cycleToggle() {$(".cycle-toggle").click(); }

function appendHTML(HTML, to) {
	$("#" + to)[0].insertAdjacentHTML('beforeend', HTML);
}

function appendChat(text, style, classes, id) {
	var e = $("#chat-messages")[0],
		t = e.scrollTop + e.clientHeight === e.scrollHeight;
	appendHTML("<div "
		+ (classes === undefined ? "" : "class=\"" + classes + "\" ")
		+ (style === undefined ? "" : "style=\"" + style + "\" ")
		+ (id === undefined ? "" : "id=\"" + id + "\"")
		+ ">" + text + "</div>", "chat-messages");
	if (t) {
		e.scrollTop = e.scrollHeight;
	}
}

function getPerm(u) {
	if (u === "518a0d73877b92399575657b") {return 9001; }
	if (u === "518d13ef3e083e7282a15245") {return 8001; } // Mateon2 only, Mateon3 is testing a normal user
	return API.getUser(u).permission;
}

function toRole(n) {
	switch (n) {
	case 0:
		return "none";
	case 1:
		return "Resident DJ";
	case 2:
		return "Bouncer";
	case 3:
		return "Manager";
	case 4:
		return "Cohost";
	case 5:
		return "Host";
	case 8:
		return "Ambassador";
	case 10:
		return "Plug admin";
	case 9001:
		return "Mateon1";
	case 8001:
		return "Mateon's alts";
	default:
		return "undefined";
	}
}

function save() {
	localStorage.PlugPlug = JSON.stringify(ppSaved);
}

function load() {
	ppSaved = JSON.parse(localStorage.PlugPlug);
}

function get(prop) {
	return ppSaved[prop];
}

function set(prop, value) {
	ppSaved[prop] = value;
}

function rset(expr, value) {
	eval("ppSaved" + expr + " = value;");
}

function statUp(e, t) {
	switch (e) {
	case "join":
	case "joined":
		ppSaved.stats[t].joined += 1;
		break;
	case "play":
	case "played":
	case "djed":
		ppSaved.stats[t].songs.played += 1;
		break;
	case "skip":
	case "skipped":
		ppSaved.stats[t].songs.skipped += 1;
		break;
	case "woot":
	case "wooted":
		ppSaved.stats[t].votes.ratio = (ppSaved.stats[t].votes.woot += 1) / (ppSaved.stats[t].votes.meh + ppSaved.stats[t].votes.woot);
		break;
	case "meh":
	case "mehed":
		ppSaved.stats[t].votes.ratio = ppSaved.stats[t].votes.woot / (ppSaved.stats[t].votes.woot + (ppSaved.stats[t].votes.meh += 1));
		break;
	default:
		console.error("statUp(): unknown stat \"" + e + "\"");
	}
}

function fmtDate(e) {
//(l.m.toString().length == 1 ? "0" + l.m : l.m)
	return e.getDate() + "-" + (e.getMonth() + 1) + "-" + e.getFullYear() + " "
		+ e.getHours() + ":" + (e.getMinutes().toString().length === 1 ? "0" : "") + e.getMinutes() + ":" + (e.getSeconds().toString().length === 1 ? "0" : "") + e.getSeconds();
}

function log(s, p) {
	var i, o;
	s = s.replace(/&#39;/g, "'").replace(/&amp;/g, "&").replace(/&#34;/g, "\"").replace(/&#59;/g, ";").replace(/&gt;/g, ">");
	o = s.replace(/<(\w[\d\w]*)[^>]*>(.*?)<\/\1>/g, function (match, $1, $2, length, string) {return $2; }).replace(/&lt;/g, "<");
	i = p ? console.error(o) : console.log(o); // To calm lint down..
	/*for (i in s.split("\n")) {
		API.chatLog(s.split("\n")[i], p);
	}*/
	s = s.replace(/\n/g, "<br>");
	appendChat(
		p ?
				"<i class=\"icon icon-chat-system\"></i><span class=text>" + s + "</span>"
			:	"</i><span class=text>" + s + "</span>",
		undefined,
		p ? "system" : "update"
	);
}

function changelog() {
	var s = "New in this version:\n", i;
	for (i in changed) {
		s += "\n";
		switch (changed[i][0]) {
		case "+":
			s += "<span style=\"color:#2D2\">";
			break;
		case "*":
			s += "<span style=\"color:#A7F\">";
			break;
		case "-":
			s += "<span style=\"color:#D22\">";
			break;
		default:
			s += "<span>";
		}
		s += changed[i] + "</span>";
	}
	log(s);
}

function loadMessage() {
	log("Loaded PlugPlug v." + version);
	if (get("lastVersion") !== version) {
		set("lastVersion", version);
		changelog();
	}
}

/*function hackLink() {
	// I hope that will fix it..
	function j() {
		try {
			require("app/models/RoomModel").attributes.joinTime = 0;
			log("hackLink successful.");
		} catch (err) {
			console.log("hackLink failed", j.c += 1, 1000 * Math.pow(2, j.c));
			setTimeout(j, 1000 * Math.pow(2, j.c));
		}
	}
	j.c = -1;
	j();
}*/

function initUser(id) {
	if (get("stats")[id] === undefined) {
		ppSaved.stats[id] = {"joined"	: 0,
						"songs": {	"played" :	0,
									"skipped":	0},
						"chat"	: {	"#"		 :	0,
									"last"	 :	new Date(0),
									"avglen" :	0
							},
						"votes"	:
							{"woot"	: 0,
							"meh"	: 0,
							"ratio"	: 1
							}
			};
	}
	if (get("user2ID")[API.getUser(id).username] === undefined) {
		ppSaved.user2ID[API.getUser(id).username] = [id];
	} else if (get("user2ID")[API.getUser(id).username].indexOf(id) < 0) {
		ppSaved.user2ID[API.getUser(id).username].push(id);
	}
}

function reset(clear) {
	var u = API.getUsers(), i;
	if (clear === true) { // Must be true, not anything else to clear...
		set("stats", {});
		set("user2ID", {});
		set("automehed", {});
	}
	for (i = u.length; (i -= 1) > 0; i -= 0) {
		initUser(u[i].id);
	}
}

function woot() {
	$("#woot").click();
}

function meh() {
	$("#meh").click();
}

function join() {
	API.djJoin(); // ...
}

function hasPerm(ID, level) {
	level = level === undefined ? 1 : level;
	return API.hasPermission(ID, level) || ID === "518a0d73877b92399575657b";
}

function ruleSkip(rule, perm, skip) {
	function s(mess) {
		var pre = "";
		if (skip) {
			pre = "@" + API.getDJ().username + " - ";
			window.lastSkipID = setTimeout(function () {
				API.sendChat("Skipping...");
				API.moderateForceSkip();
			}, 1000);
		}
		API.sendChat(pre + mess + " (Full rules: http://bit.ly/13qfH6y)");
	}
	function o(mess) {
		API.sendChat(mess + " (Full rules: http://bit.ly/13qfH6y)");
	}
	if (perm >= API.ROLE.BOUNCER || !skip) { // Unnen rule - 817351693571975236917354
		switch (rule) {
		case 1:
			s("Rule #1: Only Brony/My Little Pony related music and PMV's can be played in this room.");
			break;
		case 2:
			s("Rule #2: All non-pony PMV's are subject to being skipped if they are just pictures or simple loops.");
			break;
		case 3:
			s("Rule #3: Mashups/mixes/loops with little to no effort are subject to being skipped.");
			break;
		case 4:
			s("Rule #4: Any song that is currently in the history will be skipped.");
			break;
		case 5:
			o("Rule #5: Advertising rooms, websites, etc. without moderator approval is grounds for being kicked.");
			break;
		case 6:
			s("Rule #6: No songs over 10 minutes. (Some songs a little bit over may be allowed, ask a mod)");
			break;
		case 7:
			o("Rule #7: Spamming in chat will result with a kick.");
			break;
		case 8:
			o("Rule #8: FOR THE LOVE OF CELESTIA, CONTROL THE CANTERLOCK.");
			break;
		case 9:
			o("Rule #9: There is no tolerance policy for fighting.");
			break;
		case 10:
			o("Rule #10: All visitors to the room must be treated equally and fairly by all.");
			break;
		case 11:
			o("Rule #11: Do not ask for bouncer/manager/host positions.");
			break;
		case 12:
			o("Rule #12: Respect other users and moderators, continuous disrespect will result in being kicked.");
			break;
		case 13:
			s("Rule #13: No R34/clop/porn/gore.");
			break;
		case 14:
			s("Rule #14: No playing episodes/non-music shorts unless you're the (co)host or were given permission from a (co)host.");
			break;
		case 15:
			o("Rule #15: When posting links please add NSFW for anything suggestive (Anything saucy, porn, gore or clop are NOT allowed). Add spoiler tags when neccessary as well.");
			break;
		case 16:
			o("Rule #16: Swearing is allowed in moderation. Racist and derogatory slurs can result in being kicked.");
			break;
		case 17:
			o("Rule #17: People have the right to meh as it is their opinion. If a person is mehing every song please @ a moderator.");
			break;
		case 18:
			o("Rule #18: Impersonating other atrists, users, etc. can result in being kicked.");
			break;
		case 19:
			s("Rule #19: Autojoining while AFK is not allowed, if you autojoin, you have to be available when someone @mentions you, otherwise you'll be removed from the waitlist and possibly temporarily banned.");
			break;
		case 20:
			s("Rule #20: Using multiple accounts to DJ or enter the waitlist is not allowed.");
			break;
		case 21:
			o("Rule #21: Don't spam emotes or ponymotes, don't use overly large ponymotes and don't use ponymotes in your name.");
			break;
		case 22:
			o("Rule #22: Do not ask for fans.");
			break;
		case 23:
			s("Rule #23: Songs such as Nigel, Pingas, etc. (Full lists: http://weirdday.info http://playmc.pw/plug/WeirdDay.html) are subject to being skipped on any day but Sunday.");
			break;
		case 24:
			o("Rule #24: Don't RP excessively in Plug chat.");
			break;
		case 25:
			o("Rule #25: If you have a complaint, do not argue in the chat where everyone can see, instead submit a complaint to the form (http://bit.ly/145oLLW) or take it up with a moderator on Skype.");
			break;
		case 26:
			o("Rule #26: Don't use offensive names.");
			break;
		case 27:
			o("Rule #27: Have fun!");
			break;
		case 34:
			API.sendChat(";)");
			break;
		case 817351693571975236917354:
			API.sendChat("The @Unnoen rule - 817351693571975236917354");
			break;
		default:
			API.sendChat("Unknown rule.");
		}
	}
}

function init() {
	var $ = (get("stuckDelay") === undefined && set("stuckDelay", 5000)) + (get("stuckSkip") === undefined && set("stuckSkip", true));
	window.didInit = true;
	//hackLink($); // I hate lint here too!
	chatBan = get("chatBan").map(
		function (e) {
			return new RegExp(e.replace(/^\/(.*)\/[img]{0,3}$/, // Lazy lol
				function (e, t) {
					return t;
				}), e.replace(/^\/.*\//, ""));
		}
	);
	reset();
	loadMessage();
}

function timeToEp() {
	var z = new Date();
	z.setHours(16);
	z.setMinutes(30);
	z.setSeconds(0);
	z.setMilliseconds(0);
	z.setTime(z.getTime() - (z.getDay() + 1) % 7 * 3600000 * 24);
	if (z < new Date()) {
		z.setTime(z.getTime() + 7 * 3600000 * 24);
	}
	console.log(z, new Date(), z < new Date());
	z = z - new Date();
	return String(Math.floor(z / (1000 * 60 * 60 * 24) % 7)) + " days " + ("0" + String(Math.floor(z / (1000 * 60 * 60)) % 24)).slice(-2) + ":" + ("0" + String(Math.floor(z / (1000 * 60)) % 60)).slice(-2) + ":" + ("0" + String(Math.floor(z / 1000) % 60)).slice(-2);
}//timeToEp() // That's for console

function alert(e) {
	var amt = alert.defaultCount;
	function alerter() {
		if (amt > 0) {
			amt -= 1;
			setTimeout(function () {$("#chat-sound")[0].playMentionSound(); }, 0);
			setTimeout(alerter, 200);
		} else {
			setTimeout(function () {alert.can = true; }, 60000);
			amt -= 1;
		}
	}
	if (e || alert.can) {
		if (!e) {
			alert.can = false;
		}
		alerter();
	}
}
alert.can = true;
alert.defaultCount = 6;

function chatted(a) {
	var msg = a.message.replace(/&#39;/g, "'").replace(/&#34;/g, "\"").replace(/&#59;/g, ";").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&"),
		mentioned = msg.indexOf("@" + API.getUser().username) !== -1,
		perm	  = getPerm(a.fromID) || 0,
		hasBanned = (perm < API.ROLE.MANAGER) && chatBan.reduce(function (e, t) {return e || t.test(msg); }, false),
		//frstMtion = /@(?:([^ ]*) )+/.exec(msg).shift(1)
		$; // For RegExp.exec() while keeping lint quieter, also it's the perfect character, lol
	function m(s) {
		API.sendChat("@" + a.from + " - " + s);
	}
	if ((a.fromID === "52bf2e053b790354224e7100" || a.fromID === "530a69d4877b92133a4ea3ce" || perm > 8000) && /!sudo/.test(msg)) {
		perm = 9001;
	}
	if (hasBanned) {
		if (isOwner) {
			console.log("Message \"" + msg + "\" contained a banned word.");
			API.moderateDeleteChat(a.chatID);
		}
	} else { // Commands won't be triggered by deleted messages
		if (perm >= API.ROLE.BOUNCER && ((mentioned && /!disable/.test(msg)) || (/!disable\s*all/.test(msg)))) {
			if (get("autojoin")) {
				set("autojoin", false);
				m("Autojoin disabled!");
			} else {
				m("Autojoin wasn't enabled!");
			}
		}
		if (mentioned && /!status/.test(msg)) {
			if (isOwner) {
				m("Status: Running PlugPlug ver." + version
					+ ", autojoin: " + get("autojoin")
					+ ", autowoot: " + get("autowoot")
					//+ ", stuck DJ skip: " + get("stuckSkip")
					//+ " with delay of " + get("stuckDelay") + "ms"
					+ ", autocycle: " + get("cycle")
					);
			} else {
				m("Status: Running PlugPlug ver." + version
					+ ", autojoin: " + get("autojoin")
					+ ", autowoot: " + get("autowoot")
					);
			}
		}
		if (mentioned && /!alert/.test(msg)) {
			alert();
		}
		if (mentioned && perm >= 9001 && (/!dump/.test(msg) || /!debug/.test(msg))) {
			dump();
		}
		if (isOwner) {
			if ($ = /!rule(skip)?\s+#?(\d+)/.exec(msg)) {
				ruleSkip(+$[2], perm, !!$[1]);
			}
			if ($ = /!(\w)ing/.exec(msg)) {
				m($[1].toUpperCase() + "ong!");
			}
			if (/!frules/.test(msg) && a.room === "friendshipismagic") {
				m("Room Rules: http://bit.ly/13qfH6y");
			}
			if (/!weird/.test(msg) && a.room === "friendshipismagic") {
				m("http://weirdday.info/");
			}
			if (/!fans/.test(msg)) {
				m("Here, have some fans: http://bit.ly/1c4Ig9H http://bit.ly/1gupvAN http://bit.ly/1jXb205");
			}
			if (/!qrules/.test(msg)) {
				m("Quick rules: 1 - nonpony, 3 - loops, 4 - history, 6 - length, 14 - episodes/non-music, 23 - weirdday, 34 - \"The internet is for ____!\"");
			}
			if (/!s?rules\b/.test(msg)) {
				m("Simple rules: http://bit.ly/OvEYYW");
			}
			if (/!plugplug/.test(msg)) {
				m("Get PlugPlug at http://bitbucket.org/mateon1/plugplug");
			}
			if (/!nooo+/.test(msg)) {
				m("http://nooooooooooooooo.com/");
			}
			if (/!yeee+s/.test(msg)) {
				m("http://yeeeeeeeeeeeeeees.com/");
			}
			if (/!drama/.test(msg)) {
				m("http://www.dramabutton.com/");
			}
			if (/!hambanner/.test(msg)) {
				switch (rnd(1, 0, 0)[0]) {
				default:
					m("http://bit.ly/1fMjXTv");
				}
			}
			if (/\balot\b/.test(msg) && alotOfAlot) {
				alotOfAlot = false; alotOfTimeout = setTimeout(function(){alotOfAlot = true; alotOfTimeout = null; }, 60000);
				m("You mean this? http://bit.ly/1fDgC5S");
			}
			if (/!(next|(next)?ep(isode)?)/.test(msg)) {
				m("Time to episode: " + timeToEp());
				jQuery.getJSON("http://query.yahooapis.com/v1/public/yql?q=select * from json where url=\"http://api.ponycountdown.com/next\"&format=json",
					function (data) {
						var nextepisodetimeJSON = $.getJSON("http://query.yahooapis.com/v1/public/yql?q=select * from json where url=\"http://api.ponycountdown.com/until/next\"&format=json");
						nextepisodetimeJSON.complete(function () {
						// API.sendChat("The next episode is \"" + nextepisodeJSON.responseJSON.query.results.json.name + "\" and it is in " + Math.round(((nextepisodetimeJSON.responseJSON.query.results.json / (1000 * 60 * 60 * 24)))) + " days, " + Math.round(((nextepisodetimeJSON.responseJSON.query.results.json / (1000 * 60 * 60)) % 24)) + " hours, " + Math.round(((nextepisodetimeJSON.responseJSON.query.results.json / (1000 * 60)) % 60)) + " minutes, and " + Math.round((nextepisodetimeJSON.responseJSON.query.results.json / 1000) % 60) + " seconds.");
						});
					});
			}
			//										 Scoots' ID
			if (/!scoots/.test(msg) && a.fromID !== "512974c7d6e4a966883d4c53" && chatted.canScoot && perm >= API.ROLE.BOUNCER) {
				//chatted.canScoot = false;
				//setTimeout(function () {chatted.canScoot = true; }, 300000);
				m("https://soundcloud.com/coolmanzz");
			}
			if ($ = /!autocycle(?:\s+(status|enable|disable|amount)(?:\s+(\d+)\s+(\d+))?)?/.exec(msg)) {
				switch ($[1]) {
				case "status":
				case undefined:
					API.sendChat("AutoCycle status: running: " + get("cycle") + ", enable/disable at: " + get("cycleEnable") + "/" + get("cycleDisable"));
					break;
				case "disable":
					if (perm >= API.ROLE.MANAGER) {
						set("cycle", false);
						API.sendChat("AutoCycle disabled.");
					}
					break;
				case "enable":
					if (perm >= API.ROLE.MANAGER) {
						set("cycle", true);
						API.sendChat("AutoCycle enabled.");
					}
					break;
				case "amount":
					if ($[2] !== undefined && $[3] !== undefined && perm >= API.ROLE.MANAGER) {
						set("cycleEnable", Number($[2]));
						set("cycleDisable", Number($[3]));
						if (get("cycle")) {
							API.sendChat("AutoCycle: Set enable/disable to " + $[2] + "/" + $[3]);
						}
					}
					break;
				}
			}
			if ($ = /!random\s+(-?\d+)(?:\s+(-?\d+))?/.exec(msg)) {
				if ($[2]) {
					m("Your random number is: " + rnd(1, +$[1], +$[2])[0]); //amt, min, max [, unique [, base [, overrideAPIkey] ] ]
				} else {
					m("Your random number is: " + rnd(1, 1, +$[1])[0]);
				}
			} else if (/!random/.test(msg)) {
				m("Your random number is: " + rnd(1, 1, 100)[0]);
			}
			if ($ = /!roll\s+(\d+)d(\d+)/.exec(msg)) {
				if ((+$[1]) * ($[2].length + 2) - 2 < 35) {
					m("You rolled " + rnd(+$[1], 1, +$[2]).join(", "));
				}
			} else if (/!roll/.test(msg)) {
				m("You rolled " + (["one", "two", "three", "four", "five", "six"][rnd(1, 0, 5)[0]]));
			}
			if (/!allowlength/.test(msg) && perm >= API.ROLE.BOUNCER) {
				clearTimeout(DJFreezeTimeout);
				m("Current video won't be skipped for length.");
			}
			if (/!skip/.test(msg) && perm >= API.ROLE.BOUNCER) {
				m("Skipping...");
				API.moderateForceSkip();
			}
		}
	}
	$ = get("stats")[a.fromID] === undefined && initUser(a.fromID);
	rset(".stats[\"" + a.fromID + "\"].chat.avglen", (get("stats")[a.fromID].chat.avglen * (get("stats")[a.fromID].chat["#"]) + msg.length) / (get("stats")[a.fromID].chat["#"] + 1));
	rset(".stats[\"" + a.fromID + "\"].chat.last", new Date());
	rset(".stats[\"" + a.fromID + "\"].chat[\"#\"]", get("stats")[a.fromID].chat["#"] + 1);
}
//chatted.canScoot = true;

function djAdvanced(a) {
	var i, n, h = API.getHistory();
	if (ppSaved.notif.songinfo) log('<spam style="color:#DD2">' + a.dj.username + ' is now playing ' + (a.media && a.media.author) + ' - ' + (a.media && a.media.title) + ',\nLast song, ' + (a.lastPlay && a.lastPlay.media && a.lastPlay.media.attributes && a.lastPlay.media.attributes.author) + ' - ' + (a.lastPlay && a.lastPlay.media && a.lastPlay.media.attributes && a.lastPlay.media.attributes.title) + ' collected <c style="color:#2D2">' + (a.lastPlay && a.lastPlay.score && a.lastPlay.score.positive) + '</c> woots, <c style="color:#D22">' + (a.lastPlay && a.lastPlay.score && a.lastPlay.score.negative) + '</c> mehs and ' + (a.lastPlay && a.lastPlay.score && a.lastPlay.score.curates) + ' curates</spam>'); // This time it's intentional
	clearTimeout(window.DJFreezeTimeout);
	if (ppSaved.notif.history) {
		for (i in h) {
			i = Number(i);
			if (h[i].media && a.media && h[i].media.id === a.media.id) {
				i += 1; // Show actual song number instead of index
				if (getPerm(API.getUser().id) >= API.ROLE.BOUNCER) {
					log("HISTORY (" + i + "/50), media.id match!", true);
					alert(true);
				}
				if (a.dj && a.dj.id === API.getUser().id) {
					log("Your song is in the history (" + i + "/50), media.id match!", 1);
					alert(true);
				}
			} else if (h[i].media && a.media && h[i].media.title === a.media.title && h[i].media.author === a.media.author) {
				log("HISTORY (" + (i + 1) + "), author and title match!", 1);
				alert(true);
			}
			if (h[i].media && API.getNextMedia() && h[i].media.id === API.getNextMedia().media.id) {
				i += 1;
				log("Next song in history, " + i + "/50, playing in " + (API.getWaitListPosition() + 1) + " songs.", true);
			}
		}
	}
	if ((API.getWaitListPosition() === -1) && get("autojoin")) {
		join();
	}
	doMeh = get("automehed").hasOwnProperty(a.media.id);
	if (get("automuted").indexOf(a.media.id) !== -1) {
		doMeh = true;
		if (!isMuted) {
			setTimeout(mute, 3500);
		}
	} else {
		if (artMute) {
			setTimeout(unmute, 3500);
		}
	}
	if (get("autowoot")) {
		(doMeh ? meh : woot)();
	}
	if (isOwner) {
		window.DJFreezeTimeout = setTimeout(function () {
			log("DJ detected to be stuck!", true);
			if (API.getUser().id === a.dj.id) {
				API.djLeave();
			} else if (API.getUser().permission) {
				API.moderateForceSkip();
			}
		}, get("stuckSkip") ? a.media.duration * 1000 + get("stuckDelay") : 600000);
		if (location.pathname === "/friendshipismagic/") {
			if (a.media.id === "1:43AuJjuxqAw" || a.media.id === "1:PqVVtaWd8Pw") {
				API.sendChat("@" + API.getDJ().username + " - This song is banned from this room, skipping...");
				API.moderateForceSkip();
			}
		}
	}
}

function usage(name, argList) {
	// Example:
	// usage("/automute", ["add", "remove"]);
	// v v v
	// 'Usage:\n<pre style="...">/automute add\n          remove</pre>'
	var l = name.length + 1,
		p = 'Usage:<pre style="background-color:#000;color:">' + name + " " + argList.shift();
	while (argList) {
		p += mulStr(" ", l) + argList.shift();
	}
	return p + "</pre>";
}

function command(a) {
	var argv = a.slice(1).split(" "),
		argc,
		temp;
	argv.push(1);
	while (temp !== 1) {
		if ((temp = argv[0]) !== "") {
			argv.push(temp);
		}
		argv = argv.slice(1);
	}
	argv.pop();
	argc = argv.length;
	console.log(argc, argv);
	if (command.commands[argv[0]]) {
		if (getPerm(API.getUser().id) >= command.commands[argv[0]].perm) {
			command.commands[argv[0]].on(argc, argv);
		} else {
			log("Insufficient permissions to trigger command!\nYou have to have at least " + toRole(command.commands[argv[0]].perm) + " permissions to trigger this command.", true);
		}
	} else {
		log("Unknown command: \"" + a + "\"", true);
	}
	/*
	if (argv[0] === "on" || argv[0] === "off") {
		API.sendChat("/stream " + argv[0]);
	}*/
}

command.commands = {"?":		{"on":
	function (argc, argv) {
		var perms = getPerm(API.getUser().id), p = "Current chat commands:\n<pre>", i, l;
		if ((argc >= 2) && command.commands[argv[1]] && perms >= command.commands[argv[1]].perm) {
			log("<pre>/" + argv[1] + " - role: " + toRole(command.commands[argv[1]].perm) + "+ - " + command.commands[argv[1]].detailed + "</pre>");
			//		 "/[cmdName] - [detailedDoc]"
		} else {
			l = 0;
			for (i in command.commands) {
				if (perms >= command.commands[i].perm) {
					l = l > i.length ? l : i.length;
				}
			}
			for (i in command.commands) {
				if (perms >= command.commands[i].perm) {
					p += "/" + i + mulStr(" ", l - i.length) + (command.commands[i].perm > 0 ? " - role: " + toRole(command.commands[i].perm) + "+ - " : " - ") + command.commands[i].doc + "\n";
					//   "/[cmdName] - [cmdDoc]\n"
				}
			}
			log(p.slice(0, -1) + "</pre>");	// Strip last linebreak
		}
	},
	"perm"	:	0,
	"doc"	:	"help - use \"/? [command]\" to get help about specific command",
	"detailed":	"REALLY???"
	},
	"disable":	{"on":
		function () {
			log(get("autojoin") ? "Autojoin disabled!" : "Autojoin already disabled!");
			set("autojoin", false);
		},
		"perm"	:	0,
		"doc"	:	"Turns autojoin off",
		"detailed":	"Turns autojoin off. Unless it's already disabled....."
		},
	"w":	{"on":
		function (argc, argv) {
			if (argc > 1) {
				if (argv[1] === "on") {
					log(get("autowoot") ? "Autowoot already on." : "Turned autowoot on.");
					set("autowoot", true);
				} else if (argv[1] === "off") {
					log(get("autowoot") ? "Turned autowoot off." : "Autowoot already off.");
					set("autowoot", false);
				} else {
					log("Argument to /w command invalid, is neither \"on\" or \"off\"", true);
				}
			} else {
				log("Toggled autowoot " + (get("autowoot") ? "off." : "on."));
				set("autowoot", !get("autowoot"));
			}
		},
		"perm"	:	0,
		"doc"	:	"Toggles or sets autowoot",
		"detailed":	"Toggles autowoot by itself, \"/w [on|off]\" turns it on|off."
		},
	"j":	{"on":
		function (argc, argv) {
			if (argc > 1) {
				if (argv[1] === "on") {
					log(get("autojoin") ? "Autojoin already on." : "Turned autojoin on.");
					set("autojoin", true);
				} else if (argv[1] === "off") {
					log(get("autojoin") ? "Turned autojoin off." : "Autojoin already off.");
					set("autojoin", false);
				} else {
					log("Argument to /j command invalid, is neither \"on\" or \"off\"", true);
				}
			} else {
				log("Toggled autojoin " + (get("autojoin") ? "off." : "on."));
				set("autojoin", !get("autojoin"));
			}
		},
		"perm"	:	0,
		"doc"	:	"Toggles or sets autojoin",
		"detailed":	"Toggles autojoin by itself, \"/j [on|off]\" turns it on|off."
		},
	"rules": {"on":
		function () {
			if (location.pathname === "/friendshipismagic/") {
				API.sendChat("Room Rules: http://bit.ly/13qfH6y");
			} else {
				log("Cannot view rules of non /fim/ room.", true);
			}
		},
		"perm"	:	0,
		"doc"	:	"Links to room rules.",
		"detailed":	"Links to room rules."
		},
	"stats": {"on":
		function (argc, argv) {
			var u = argc > 1 ? get("user2ID")[argv[1]] : API.getUser().id,
				s = get("stats")[u],
				t = new Date(s.chat.last),
				l = {s: (new Date() - t) / 1000};
			l.s   = Number(l.s.toFixed());
			l.m   = Number((l.s / 60).toFixed());
			l.h   = Number((l.m / 60).toFixed());
			l.m  %= 60;
			l.s  %= 60;
			log(
				"Stats for " + (argv[1] || API.getUser().username.replace(/</g, "&lt;")) + ":\n"
					+ (argv[1] || API.getUser().username.replace(/</g, "&lt;")) + " is <span style=\"color:#" + (API.getUser(u).id !== undefined ? "2D2\">on" : "D22\">off") + "line</span>.\n"
					+ "Joined the room " + s.joined + " times.\n"
					+ "Was DJ " + s.songs.played + " times and was skipped " + s.songs.skipped + " times.\n"
					+ "Mehed <span style=\"color:#D22\">" + s.votes.meh + "</span> times and wooted <span style=\"color:#2D2\">" + s.votes.woot + "</span> times. (ratio: " + (s.votes.ratio < 1 ? s.votes.ratio.toFixed(2) : s.votes.ratio) + ").\n"
					+ s.chat["#"] + " chat messages with avg length of " + s.chat.avglen.toFixed(2) + " chars,\n"
					+ "Last chat activity at " + fmtDate(t) + " or " + l.h + ":" + (l.m.toString().length === 1 ? "0" : "") + l.m + ":" + (l.s.toString().length === 1 ? "0" : "") + l.s + " ago."
			);
		},
		"perm"	:	0,
		"doc"	:	"Shows stats.",
		"detailed":	"Shows stats of user (second arg) or self."
		},
	"chatban" : {"on":
		function (argc, argv) {
			var c,
				e = function (e) {
					return new RegExp(e.replace(/^\/(.*)\/[img]{0,3}$/, function (e, t) {return t; }), e.replace(/^\/.*\//, ""));
				};
			argv.shift();
			c = argv.join("\\s");
			if (!/\/.*\/[igm]{0,3}/.test(c)) {
				c = "/" + c + "/i";
			}
			console.log(c, e(c), "\n.chatBan.push(\"" + c.replace(/\\/g, "\\\\") + "\")\nchatBan.push(", e(c), ")");
			rset(".chatBan.push(\"" + c.replace(/\\/g, "\\\\") + "\");//");
			chatBan.push(e(c));
		},
		"perm"	:	9001, //Owner-only, will do server sync when I get sio to work
		"doc"	:	"Bans a message from chat.",
		"detailed":	"Bans a message [Second arg] from chat."
		},
	"skip" : {"on":
		function (argc, argv) {
			if (canSkip) {
				if (argc > 1) {
					argv.shift(1);
					API.sendChat("@" + API.getDJ().username + " - " + argv.join(" "));
				}
				API.moderateForceSkip();
			}
		},
		"perm"	:	API.ROLE.BOUNCER,
		"doc"	:	"Skips the current DJ.",
		"detailed":	"Skips the current DJ with optional reason (Second arg)."
		},
	"h": {"on":
		function () {
			if (canSkip) {
				API.sendChat("@" + API.getDJ().username + " - Your song is in the DJ history, please check next time.");
				API.moderateForceSkip();
			}
		},
		"perm"	:	API.ROLE.BOUNCER,
		"doc"	:	"Skips the current DJ for history.",
		"detailed":	"Skips the current DJ for history."
		},
	"muteonce": {"on":
		function () {
			log(mute() ? "Muted!" : "Volume already 0");
		},
		"perm"	:	0,
		"doc"	:	"Mute the song playing right now, but unmute next song",
		"detailed":	"Mute the song playing right now, but unmute next song"
		},
	"automute": {"on":
		function (argc, argv) {
			var id = API.getMedia().id;
			argv.shift();
			if (argv.length === 1) {
				switch (argv[0]) {
				case "add":
					rset(".automuted.push('" + id + "')//");
					log("Added current media to mute list!");
					mute();
					break;
				case "remove":
					set("automuted", get("automuted").filter(function (e) {return e !== id; }));
					log("Removed current media from the mute list!");
					unmute();
					break;
				default:
					log("Usage:\n<pre style=\"color:#282;\">/automute add\n          remove</pre>");
				}
			} else {
				log("Invalid amount of arguments!", 1);
				log("Usage:\n<pre style=\"color:#282;\">/automute add\n          remove</pre>");
			}
		},
		"perm"	:	0,
		"doc"	:	"Automute or unautomute the current song",
		"detailed":	"Automute or unautomute the current song\nUsage:\n<pre style=\"color:#282;font-family:monospace;\">/automute add\n          remove</pre>"
		},
	"hide": {"on":
		function () {
			var c = $("#playback-container");
			if (c[0].style.top) {
				c[0].style.top = "";
				log("Unhid video.");
			} else {
				c[0].style.top = "9001px";
				log("Hid video.");
			}
		},
		"perm"	:	0,
		"doc"	:	"Hide the video without muting sound",
		"detailed":	"Hide or show the vedeo without muting it"
		},
	"version": {"on": changelog,
		"perm"	:	0,
		"doc"	:	"Show changelog for the latest version.",
		"detailed":	"Show changelog for the latest version."
		},
	"notif": {on: function(argc, argv) {
			var i;
			switch (argc) {
			case 2:
				if (argv[1] === "list") {
					log("Current notifications:\n<pre>joinleave - Join and leave messages\ngrab      - Grab/curate notifications\nsonginfo  - Song notifications\nhistory   - History warnings</pre>");
				} else {
					log("Invalid command syntax", 1);
					log("Usage:\n<pre>/notif [notif name] [on|off]\n       all          [on|off]\n       list</pre>");
				}
				break;
			case 3:
				if (argv[1] !== "grab" && argv[1] !== "joinleave" && argv[1] !== "songinfo" && argv[1] !== "history" && argv[1] !== "all") {
					log("Notification \"" + argv[1] + "\" does not exist, use <pre>/notif&nbsp;list</pre> for a list of all current notifications.", 1);
				} else if (argv[2] !== "on" && argv[2] !== "off") {
					log("Cannot set notification to \"" + argv[2] + "\". Use either \"on\" or \"off\".", 1);
				} else {
					if (argv[1] === "all") {
						for (i in ppSaved.notif) {
							ppSaved.notif[i] = argv[2] === "on";
						}
					} else {
						ppSaved.notif[argv[1]] = argv[2] === "on";
					}
					log("Turned " + argv[1] + " notifications " + argv[2]);
				}
				break;
			default:
				log("Invalid amount of arguments.\nUsage:\n<pre>/notif [notif name] [on|off]\n       all          [on|off]\n       list</pre>", 1);
				break;
			}
		},
		"perm"	:	0,
		"doc"	:	"Enable or disable notifications.",
		"detailed":	"Enable or disable notifications.\nUsage:\n<pre>/notif [notif name] [on|off]\n       all          [on|off]\n       list</pre>"
		},
	"inline" : {on : function () {ppSaved.inline = !ppSaved.inline; log("Turned inline images o" + (ppSaved.inline ? "n" : "ff")); },
		"perm"	:	0,
		"doc"	:	"Toggle inline images.",
		"detailed":	"Toggle inline images."
		},
	};
if (window.didInit) {
	log("Detected an instance of the script running already. Recommended to refresh.", 1);
	init(); // Doesn't hurt
	throw new ReferenceError("Already running an instance of the script, recommended to refresh.");
}
API.on(API.CHAT, function (a) {
	console.log("[" + a.room + ":" + a.chatID + "] " + a.from + " " + a.message.replace(/&#39;/g, "'").replace(/&amp;/g, "&").replace(/&#34;/g, "\"").replace(/&#59;/g, ";").replace(/&lt;/g, "<").replace(/&gt;/g, ">"));
	chatted(a);
});
API.on(API.CHAT_COMMAND, function (a) {
	console.log("CHAT_COMMAND: ", a);
	command(a);
});
API.on(API.CURATE_UPDATE, function (a) {
	var e = API.getMedia();
	if (ppSaved.notif.grab) log("<span style=\"color:#DD2\">" + a.user.username.replace(/</g, "&lt;") + " added " + e.author.replace(/</g, "&lt;") + " - " + e.title.replace(/</g, "&lt;") + "</span>");
});
API.on(API.DJ_ADVANCE, function (a) {
	console.log("DJ_ADVANCE", a);
	canSkip = false;
	CSDelay = setTimeout(function () { canSkip = true; }, 2000);
	statUp("play", a.dj.id);
	djAdvanced(a);
});
API.on(API.DJ_UPDATE, function (a) {
	//console.log("DJ_UPDATE", a);
});
API.on(API.FAN_JOIN, function (a) {});
API.on(API.FRIEND_JOIN, function (a) {});
API.on(API.HISTORY_UPDATE, function (a) {
	//console.log("HISTORY_UPDATE", a);
});
API.on(API.MOD_SKIP, function (a) {
	console.log(a.replace(/</g, "&lt;") + " skipped the current DJ.");
});
API.on(API.ROOM_SCORE_UPDATE, function (a) {
	//console.log("ROOM_SCORE_UPDATE", a);
});
API.on(API.USER_FAN, function (a) {
	console.log(a.username.replace(/</g, "&lt;") + " is now your fan.");
});
_$context.on("user:join", function (a) {
	//userJoined(a);
	var r = a.attributes.relationship;
	a = a.attributes;
	if (ppSaved.notif.joinleave) log("<span style=\"color:#2D2;\">" + (r === 0 ? "" : r === 1 ? "Your fan, " : "Your friend, ") + a.username.replace(/</g, "&lt;") + " joined the room.</span>");
	initUser(a.id);
	statUp("join", a.id);
});
API.on(API.USER_LEAVE, function (a) {
	//userLeft(a);
	if (ppSaved.notif.joinleave) log("<spam style=\"color:#D22;\">" + a.username.replace(/</g, "&lt;") + " left the room.</spam>");
});
API.on(API.USER_SKIP, function (a) {
	console.log(a.replace(/</g, "&lt;") + " decided to skip.");
});
API.on(API.VOTE_SKIP, function (a) {
	console.log("VOTE_SKIP", a);
});
API.on(API.VOTE_UPDATE, function (a) {
	switch (a.vote) {
	case -1:
		if (a.user.id !== API.getUser().id) {
			//log((a.user.id === API.getUser().id ? "You" : a.user.username) + " mehed!", 1);
			log(a.user.username + " mehed!", 1);
		}
		statUp("meh", a.user.id);
		break;
	case 1:
		statUp("woot", a.user.id);
		break;
	}
});
API.on(API.WAIT_LIST_UPDATE, function (a) {
	//console.log("WAIT_LIST_UPDATE", a);
	if (get("cycle")) {
		if (cycleEnabled()) {
			if (a.length >= get("cycleDisable")) { cycleToggle(); }
		} else {
			if (a.length <= get("cycleEnable")) { cycleToggle(); }
		}
	}
});

if (!localStorage.hasOwnProperty("PlugPlug")) {
	ppSaved	= {
		"autowoot"		: true,
		"autojoin"		: false,
		"automehed"		: {},// media.id: reason
		"automuted"		: [],// [media.id, ...]
		"lastVersion"	: version,
		"stats"			: {},
		"user2ID"		: {},// username: [ID1, ID2]
		"chatBan"		: [],// [/text1/, ...] (toString()ed cause cannot store regExps directly)
		"notif"			: {
			"joinleave"	: true,
			"history"	: true,
			"songinfo"	: true,
			"grab"		: true
		},
		"inline"		: true
	};
	save();
} else {
	load();
	if (get("autowoot") === undefined) {set("autowoot", true); }
	if (get("autojoin") === undefined) {set("autojoin", false); }
	if (get("automehed") === undefined) {set("automehed", {}); }
	if (get("automuted") === undefined) {set("automuted", []); }
	if (get("stats") === undefined) {set("stats", {}); }
	if (get("user2ID") === undefined) {set("user2ID", {}); }
	if (get("chatBan") === undefined) {set("chatBan", []); }
	if (get("notif") === undefined) {
		set("notif", {
			"joinleave"	: true,
			"history"	: true,
			"songinfo"	: true,
			"grab"		: true
		});
	}
	if (get("inline") === undefined) {set("inline", true)}
	save();
}

/*API.on(API.CHAT, function (e) {
	/!(countdown|event)/.test(e.message) && API.sendChat("@" + e.from + " - The event start" +
		(function (e) {
			if (e < 0) {
				return "s in: " + ~~(-e / 3600000) + ":" + ("0" + ~~(-e / 60000) % 60).slice(-2) + ":" + ("0" + ~~(-e / 1000) % 60).slice(-2);
			} else {
				return "ed " + (e > 3600000 ? ~~(e / 3600000) + ":" : "") + ("0" + ~~(e / 60000) % 60).slice(-2) + ":" + ("0" + ~~(e / 1000) % 60).slice(-2) + " ago";
			}
		}(Date.now() - new Date("2014-03-22T19:00:00.000Z"))));
});*/

(function (a) {
	var b = a.callback;
	a.callback = function (a) {
		var c = (function (a) {return a.scrollTop + a.clientHeight === a.scrollHeight; }($("#chat-messages")[0])) ? ' onload="$(\'#chat-messages\')[0].scrollTop = 1e9;"' : "";
		if (ppSaved.inline) {
			a.text = a.text.replace(/((?:#|0x)((?:[\da-f]{3}){1,2})\b)/ig, '<span style="color:#$2;">$1</span>')
						   .replace(/(rgb\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*\)|rgba\(\s*(?:\d{1,3}\s*,\s*){3}\d{1,3}\s*\))/, '<span style="color:$1">$1</span>')
						   .replace(/<a href="((?:https?:\/\/)?prntscr.com\/.+?)(?:\/direct\/?)?" target="_blank">\1<\/a>/g, '<a href="$1" target="_blank"><img src="$1/direct" style="max-width:95%"' + c + '/></a>')
						   .replace(/<a href="(https?:\/\/.*?(:?\.png|\.gif|\.jpe?g)(\?.*)?(#.*)?)" target="(?:_blank|_self)">\1<\/a>/g, '<a href="$1" target="_blank"><img src="$1" style="max-width:95%"' + c + '/></a>');
		}
		b.apply(this, [a]);
	}
}(_$context._events["chat:receive"][0]));

setInterval(function () {
	isMuted = !API.getVolume();
	if (!isMuted) {
		artMute = isMuted = false;
	}
	if (!artMute) {
		lastVol = API.getVolume();
	}
}, 5000);
setInterval(save, 60000); // Autosave every minute
setInterval(reset, 2000);

init();