text: 'use strict';
  
  // modules and constants
  const fs = require('fs');
  const http = require('http');
  const https = require('https');
  
  let bubbleLetterMap = new Map([
  	['a', '\u24D0'], ['b', '\u24D1'], ['c', '\u24D2'], ['d', '\u24D3'], ['e', '\u24D4'], ['f', '\u24D5'], ['g', '\u24D6'], ['h', '\u24D7'], ['i', '\u24D8'], ['j', '\u24D9'], ['k', '\u24DA'], ['l', '\u24DB'], ['m', '\u24DC'],
  	['n', '\u24DD'], ['o', '\u24DE'], ['p', '\u24DF'], ['q', '\u24E0'], ['r', '\u24E1'], ['s', '\u24E2'], ['t', '\u24E3'], ['u', '\u24E4'], ['v', '\u24E5'], ['w', '\u24E6'], ['x', '\u24E7'], ['y', '\u24E8'], ['z', '\u24E9'],
  	['A', '\u24B6'], ['B', '\u24B7'], ['C', '\u24B8'], ['D', '\u24B9'], ['E', '\u24BA'], ['F', '\u24BB'], ['G', '\u24BC'], ['H', '\u24BD'], ['I', '\u24BE'], ['J', '\u24BF'], ['K', '\u24C0'], ['L', '\u24C1'], ['M', '\u24C2'],
  	['N', '\u24C3'], ['O', '\u24C4'], ['P', '\u24C5'], ['Q', '\u24C6'], ['R', '\u24C7'], ['S', '\u24C8'], ['T', '\u24C9'], ['U', '\u24CA'], ['V', '\u24CB'], ['W', '\u24CC'], ['X', '\u24CD'], ['Y', '\u24CE'], ['Z', '\u24CF'],
  	['1', '\u2460'], ['2', '\u2461'], ['3', '\u2462'], ['4', '\u2463'], ['5', '\u2464'], ['6', '\u2465'], ['7', '\u2466'], ['8', '\u2467'], ['9', '\u2468'], ['0', '\u24EA'],
  ]);
  
  let asciiMap = new Map([
  	['\u24D0', 'a'], ['\u24D1', 'b'], ['\u24D2', 'c'], ['\u24D3', 'd'], ['\u24D4', 'e'], ['\u24D5', 'f'], ['\u24D6', 'g'], ['\u24D7', 'h'], ['\u24D8', 'i'], ['\u24D9', 'j'], ['\u24DA', 'k'], ['\u24DB', 'l'], ['\u24DC', 'm'],
  	['\u24DD', 'n'], ['\u24DE', 'o'], ['\u24DF', 'p'], ['\u24E0', 'q'], ['\u24E1', 'r'], ['\u24E2', 's'], ['\u24E3', 't'], ['\u24E4', 'u'], ['\u24E5', 'v'], ['\u24E6', 'w'], ['\u24E7', 'x'], ['\u24E8', 'y'], ['\u24E9', 'z'],
  	['\u24B6', 'A'], ['\u24B7', 'B'], ['\u24B8', 'C'], ['\u24B9', 'D'], ['\u24BA', 'E'], ['\u24BB', 'F'], ['\u24BC', 'G'], ['\u24BD', 'H'], ['\u24BE', 'I'], ['\u24BF', 'J'], ['\u24C0', 'K'], ['\u24C1', 'L'], ['\u24C2', 'M'],
  	['\u24C3', 'N'], ['\u24C4', 'O'], ['\u24C5', 'P'], ['\u24C6', 'Q'], ['\u24C7', 'R'], ['\u24C8', 'S'], ['\u24C9', 'T'], ['\u24CA', 'U'], ['\u24CB', 'V'], ['\u24CC', 'W'], ['\u24CD', 'X'], ['\u24CE', 'Y'], ['\u24CF', 'Z'],
  	['\u2460', '1'], ['\u2461', '2'], ['\u2462', '3'], ['\u2463', '4'], ['\u2464', '5'], ['\u2465', '6'], ['\u2466', '7'], ['\u2467', '8'], ['\u2468', '9'], ['\u24EA', '0'],
  ]);
  
  // misc
  let regdateCache = {};
  let customColors = {};
  
  global.parseStatus = function (text, encoding) {
  	if (encoding) {
  		text = text.split('').map(function (char) {
  			return bubbleLetterMap.get(char);
  		}).join('');
  	} else {
  		text = text.split('').map(function (char) {
  			return asciiMap.get(char);
  		}).join('');
  	}
  	return text;
  };
  
  RPC.pmAll = function (message, pmName) {
  	pmName = (pmName ? pmName : '~Server PM [DO NOT REPLY]');
  	Users.users.forEach(curUser => {
  		curUser.send('|pm|' + pmName + '|' + curUser.getIdentity() + '|' + message);
  	});
  };
  
  RPC.regdate = function (target, callback) {
  	target = toId(target);
  	if (regdateCache[target]) return callback(regdateCache[target]);
  	let req = https.get('https://pokemonshowdown.com/users/' + target + '.json', res => {
  		let data = '';
  		res.on('data', chunk => {
  			data += chunk;
  		}).on('end', () => {
  			try {
  				data = JSON.parse(data);
  			} catch (e) {
  				return callback(false);
  			}
  			let date = data['registertime'];
  			if (date !== 0 && date.toString().length < 13) {
  				while (date.toString().length < 13) {
  					date = Number(date.toString() + '0');
  				}
  			}
  			if (date !== 0) {
  				regdateCache[target] = date;
  				RPC.saveRegdateCache();
  			}
  			callback((date === 0 ? false : date));
  		});
  	});
  	req.end();
  };
  
  RPC.reloadCSS = function () {
  	const cssPath = 'rpcenter'; // This should be the server id if Config.serverid doesn't exist. Ex: 'serverid'
  	let options = {
  		host: 'play.pokemonshowdown.com',
  		port: 80,
  		path: '/customcss.php?server=' + (Config.serverid || cssPath),
  		method: 'GET',
  	};
  	http.get(options);
  };
  
  RPC.formatName = function (name) {
  	if (Users.getExact(name) && Users(name).connected) {
  		return '<i>' + RPC.nameColor(Users.getExact(name).name, true) + '</i>';
  	} else {
  		return RPC.nameColor(name, false);
  	}
  };
  
  RPC.loadRegdateCache = function () {
  	try {
  		regdateCache = JSON.parse(fs.readFileSync('config/regdate.json', 'utf8'));
  	} catch (e) {}
  };
  RPC.loadRegdateCache();
  
  RPC.saveRegdateCache = function () {
  	fs.writeFileSync('config/regdate.json', JSON.stringify(regdateCache));
  };
  
  RPC.parseStatus = function (text, encoding) {
  	if (encoding) {
  		text = text
  			.split('')
  			.map(char => bubbleLetterMap.get(char))
  			.join('');
  	} else {
  		text = text
  			.split('')
  			.map(char => asciiMap.get(char))
  			.join('');
  	}
  	return text;
  };
  
  RPC.load = function () {
  	fs.readFile('config/customcolors.json', 'utf8', function (err, file) {
  		if (err) return;
  		customColors = JSON.parse(file);
  	});
  };
  setInterval(function () {
  	RPC.load();
  }, 500);
  
  RPC.updateColor = function () {
  	fs.writeFileSync('config/customcolors.json', JSON.stringify(customColors));
  
  	let newCss = '/* COLORS START */\n';
  
  	for (let name in customColors) {
  		newCss += RPC.generateCSS(name, customColors[name]);
  	}
  	newCss += '/* COLORS END */\n';
  
  	let file = fs.readFileSync('config/custom.css', 'utf8').split('\n');
  	if (~file.indexOf('/* COLORS START */')) file.splice(file.indexOf('/* COLORS START */'), (file.indexOf('/* COLORS END */') - file.indexOf('/* COLORS START */')) + 1);
  	fs.writeFileSync('config/custom.css', file.join('\n') + newCss);
  	RPC.reloadCSS();
  };
  
  RPC.generateCSS = function (name, color) {
  	let css = '';
  	let rooms = [];
  	name = toId(name);
  	Rooms.rooms.forEach((curRoom, id) => {
  		if (id === 'global' || curRoom.type !== 'chat' || curRoom.isPersonal) return;
  		if (!isNaN(Number(id.charAt(0)))) return;
  		rooms.push('#' + id + '-userlist-user-' + name + ' strong em');
  		rooms.push('#' + id + '-userlist-user-' + name + ' strong');
  		rooms.push('#' + id + '-userlist-user-' + name + ' span');
  	});
  	css = rooms.join(', ');
  	css += '{\ncolor: ' + color + ' !important;\n}\n';
  	css += '.chat.chatmessage-' + name + ' strong {\n';
  	css += 'color: ' + color + ' !important;\n}\n';
  	return css;
  };
  
  /*eslint-disable */
  RPC.MD5 = function (e) {
  	function t(e, t) {
  		var n, r, i, s, o;
  		i = e & 2147483648;
  		s = t & 2147483648;
  		n = e & 1073741824;
  		r = t & 1073741824;
  		o = (e & 1073741823) + (t & 1073741823);
  		return n & r ? o ^ 2147483648 ^ i ^ s : n | r ? o & 1073741824 ? o ^ 3221225472 ^ i ^ s : o ^ 1073741824 ^ i ^ s : o ^ i ^ s;
  	}
  
  	function n(e, n, r, i, s, o, u) {
  		e = t(e, t(t(n & r | ~n & i, s), u));
  		return t(e << o | e >>> 32 - o, n);
  	}
  
  	function r(e, n, r, i, s, o, u) {
  		e = t(e, t(t(n & i | r & ~i, s), u));
  		return t(e << o | e >>> 32 - o, n);
  	}
  
  	function i(e, n, r, i, s, o, u) {
  		e = t(e, t(t(n ^ r ^ i, s), u));
  		return t(e << o | e >>> 32 - o, n);
  	}
  
  	function s(e, n, r, i, s, o, u) {
  		e = t(e, t(t(r ^ (n | ~i), s), u));
  		return t(e << o | e >>> 32 - o, n);
  	}
  
  	function o(e) {
  		var t = "",
  			n = "",
  			r;
  		for (r = 0; r <= 3; r++) n = e >>> r * 8 & 255, n = "0" + n.toString(16), t += n.substr(n.length - 2, 2);
  		return t
  	}
  	var u = [],
  		a, f, l, c, h, p, d, v, e = function(e) {
  			for (var e = e.replace(/\r\n/g, "\n"), t = "", n = 0; n < e.length; n++) {
  				var r = e.charCodeAt(n);
  				r < 128 ? t += String.fromCharCode(r) : (r > 127 && r < 2048 ? t += String.fromCharCode(r >> 6 | 192) : (t += String.fromCharCode(r >> 12 | 224), t += String.fromCharCode(r >> 6 & 63 | 128)), t += String.fromCharCode(r & 63 | 128));
  			}
  			return t;
  		}(e),
  		u = function(e) {
  			var t, n = e.length;
  			t = n + 8;
  			for (var r = ((t - t % 64) / 64 + 1) * 16, i = Array(r - 1), s = 0, o = 0; o < n;) t = (o - o % 4) / 4, s = o % 4 * 8, i[t] |= e.charCodeAt(o) << s, o++;
  			i[(o - o % 4) / 4] |= 128 << o % 4 * 8;
  			i[r - 2] = n << 3;
  			i[r - 1] = n >>> 29;
  			return i;
  		}(e);
  	h = 1732584193;
  	p = 4023233417;
  	d = 2562383102;
  	v = 271733878;
  	for (e = 0; e < u.length; e += 16) a = h, f = p, l = d, c = v, h = n(h, p, d, v, u[e + 0], 7, 3614090360), v = n(v, h, p, d, u[e + 1], 12, 3905402710), d = n(d, v, h, p, u[e + 2], 17, 606105819), p = n(p, d, v, h, u[e + 3], 22, 3250441966), h = n(h, p, d, v, u[e + 4], 7, 4118548399), v = n(v, h, p, d, u[e + 5], 12, 1200080426), d = n(d, v, h, p, u[e + 6], 17, 2821735955), p = n(p, d, v, h, u[e + 7], 22, 4249261313), h = n(h, p, d, v, u[e + 8], 7, 1770035416), v = n(v, h, p, d, u[e + 9], 12, 2336552879), d = n(d, v, h, p, u[e + 10], 17, 4294925233), p = n(p, d, v, h, u[e + 11], 22, 2304563134), h = n(h, p, d, v, u[e + 12], 7, 1804603682), v = n(v, h, p, d, u[e + 13], 12, 4254626195), d = n(d, v, h, p, u[e + 14], 17, 2792965006), p = n(p, d, v, h, u[e + 15], 22, 1236535329), h = r(h, p, d, v, u[e + 1], 5, 4129170786), v = r(v, h, p, d, u[e + 6], 9, 3225465664), d = r(d, v, h, p, u[e + 11], 14, 643717713), p = r(p, d, v, h, u[e + 0], 20, 3921069994), h = r(h, p, d, v, u[e + 5], 5, 3593408605), v = r(v, h, p, d, u[e + 10], 9, 38016083), d = r(d, v, h, p, u[e + 15], 14, 3634488961), p = r(p, d, v, h, u[e + 4], 20, 3889429448), h = r(h, p, d, v, u[e + 9], 5, 568446438), v = r(v, h, p, d, u[e + 14], 9, 3275163606), d = r(d, v, h, p, u[e + 3], 14, 4107603335), p = r(p, d, v, h, u[e + 8], 20, 1163531501), h = r(h, p, d, v, u[e + 13], 5, 2850285829), v = r(v, h, p, d, u[e + 2], 9, 4243563512), d = r(d, v, h, p, u[e + 7], 14, 1735328473), p = r(p, d, v, h, u[e + 12], 20, 2368359562), h = i(h, p, d, v, u[e + 5], 4, 4294588738), v = i(v, h, p, d, u[e + 8], 11, 2272392833), d = i(d, v, h, p, u[e + 11], 16, 1839030562), p = i(p, d, v, h, u[e + 14], 23, 4259657740), h = i(h, p, d, v, u[e + 1], 4, 2763975236), v = i(v, h, p, d, u[e + 4], 11, 1272893353), d = i(d, v, h, p, u[e + 7], 16, 4139469664), p = i(p, d, v, h, u[e + 10], 23, 3200236656), h = i(h, p, d, v, u[e + 13], 4, 681279174), v = i(v, h, p, d, u[e + 0], 11, 3936430074), d = i(d, v, h, p, u[e + 3], 16, 3572445317), p = i(p, d, v, h, u[e + 6], 23, 76029189), h = i(h, p, d, v, u[e + 9], 4, 3654602809), v = i(v, h, p, d, u[e + 12], 11, 3873151461), d = i(d, v, h, p, u[e + 15], 16, 530742520), p = i(p, d, v, h, u[e + 2], 23, 3299628645), h = s(h, p, d, v, u[e + 0], 6, 4096336452), v = s(v, h, p, d, u[e + 7], 10, 1126891415), d = s(d, v, h, p, u[e + 14], 15, 2878612391), p = s(p, d, v, h, u[e + 5], 21, 4237533241), h = s(h, p, d, v, u[e + 12], 6, 1700485571), v = s(v, h, p, d, u[e + 3], 10, 2399980690), d = s(d, v, h, p, u[e + 10], 15, 4293915773), p = s(p, d, v, h, u[e + 1], 21, 2240044497), h = s(h, p, d, v, u[e + 8], 6, 1873313359), v = s(v, h, p, d, u[e + 15], 10, 4264355552), d = s(d, v, h, p, u[e + 6], 15, 2734768916), p = s(p, d, v, h, u[e + 13], 21, 1309151649), h = s(h, p, d, v, u[e + 4], 6, 4149444226), v = s(v, h, p, d, u[e + 11], 10, 3174756917), d = s(d, v, h, p, u[e + 2], 15, 718787259), p = s(p, d, v, h, u[e + 9], 21, 3951481745), h = t(h, a), p = t(p, f), d = t(d, l), v = t(v, c);
  	return (o(h) + o(p) + o(d) + o(v)).toLowerCase();
  };
  /*eslint-enable */
  let colorCache = {};
  
  // hashColor function
  RPC.hashColor = function (name) {
  	name = toId(name);
  	if (customColors[name]) return customColors[name];
  	if (colorCache[name]) return colorCache[name];
  	let hash = RPC.MD5(name);
  	let H = parseInt(hash.substr(4, 4), 16) % 360; // 0 to 360
  	let S = parseInt(hash.substr(0, 4), 16) % 50 + 40; // 40 to 89
  	let L = Math.floor(parseInt(hash.substr(8, 4), 16) % 20 + 30); // 30 to 49
  	let C = (100 - Math.abs(2 * L - 100)) * S / 100 / 100;
  	let X = C * (1 - Math.abs((H / 60) % 2 - 1));
  	let m = L / 100 - C / 2;
  
  	let R1, G1, B1;
  	switch (Math.floor(H / 60)) {
  	case 1:
  		R1 = X;
  		G1 = C;
  		B1 = 0;
  		break;
  	case 2:
  		R1 = 0;
  		G1 = C;
  		B1 = X;
  		break;
  	case 3:
  		R1 = 0;
  		G1 = X;
  		B1 = C;
  		break;
  	case 4:
  		R1 = X;
  		G1 = 0;
  		B1 = C;
  		break;
  	case 5:
  		R1 = C;
  		G1 = 0;
  		B1 = X;
  		break;
  	case 0:
  	default:
  		R1 = C;
  		G1 = X;
  		B1 = 0;
  		break;
  	}
  	let lum = (R1 + m) * 0.2126 + (G1 + m) * 0.7152 + (B1 + m) * 0.0722; // 0.05 (dark blue) to 0.93 (yellow)
  	let HLmod = (lum - 0.5) * -100; // -43 (yellow) to 45 (dark blue)
  	if (HLmod > 12) {
  		HLmod -= 12;
  	} else if (HLmod < -10) {
  		HLmod = (HLmod + 10) * 2 / 3;
  	} else {
  		HLmod = 0;
  	}
  
  	L += HLmod;
  	let Smod = 10 - Math.abs(50 - L);
  	if (HLmod > 15) Smod += (HLmod - 15) / 2;
  	S -= Smod;
  
  	let rgb = RPC.hslToRgb(H, S, L);
  	colorCache[name] = "#" + RPC.rgbToHex(rgb.r, rgb.g, rgb.b);
  	return colorCache[name];
  };
  
  RPC.hslToRgb = function (h, s, l) {
  	let r, g, b, m, c, x;
  	if (!isFinite(h)) h = 0;
  	if (!isFinite(s)) s = 0;
  	if (!isFinite(l)) l = 0;
  	h /= 60;
  	if (h < 0) h = 6 - (-h % 6);
  	h %= 6;
  	s = Math.max(0, Math.min(1, s / 100));
  	l = Math.max(0, Math.min(1, l / 100));
  	c = (1 - Math.abs((2 * l) - 1)) * s;
  	x = c * (1 - Math.abs((h % 2) - 1));
  	if (h < 1) {
  		r = c;
  		g = x;
  		b = 0;
  	} else if (h < 2) {
  		r = x;
  		g = c;
  		b = 0;
  	} else if (h < 3) {
  		r = 0;
  		g = c;
  		b = x;
  	} else if (h < 4) {
  		r = 0;
  		g = x;
  		b = c;
  	} else if (h < 5) {
  		r = x;
  		g = 0;
  		b = c;
  	} else {
  		r = c;
  		g = 0;
  		b = x;
  	}
  	m = l - c / 2;
  	r = Math.round((r + m) * 255);
  	g = Math.round((g + m) * 255);
  	b = Math.round((b + m) * 255);
  	return {
  		r: r,
  		g: g,
  		b: b,
  	};
  };
  
  RPC.rgbToHex = function (R, G, B) {
  	return RPC.toHex(R) + RPC.toHex(G) + RPC.toHex(B);
  };
  
  RPC.toHex = function (N) {
  	if (N === null || N === undefined) return "00";
  	N = parseInt(N);
  	if (N === 0 || isNaN(N)) return "00";
  	N = Math.max(0, N);
  	N = Math.min(N, 255);
  	N = Math.round(N);
  	return "0123456789ABCDEF".charAt((N - N % 16) / 16) + "0123456789ABCDEF".charAt(N % 16);
  };
  
  //Credit to SG
  RPC.nameColor = function (name, bold, userGroup) {
  	let userGroupSymbol = Users.usergroups[toId(name)] ? '<strong><font color=#948A88>' + Users.usergroups[toId(name)].substr(0, 1) + '</font></strong>' : "";
  	return (userGroup ? userGroupSymbol : "") + (bold ? "<strong>" : "") + "<font color=" + RPC.hashColor(name) + ">" + (Users(name) && Users(name).connected && Users.getExact(name) ? Chat.escapeHTML(Users.getExact(name).name) : Chat.escapeHTML(name)) + "</font>" + (bold ? "</strong>" : "");
  };
  // usage: RPC.nameColor(user.name, true) for bold OR RPC.nameColor(user.name, false) for non-bolded.
  
  RPC.messageSeniorStaff = function (message, pmName, from) {
  	pmName = (pmName ? pmName : '~Seinor Staff PM [DO NOT REPLY]');
  	from = (from ? ' (PM from ' + from + ')' : '');
  	Users.users.forEach(curUser => {
  		if (curUser.group === '~' || curUser.group === '&') {
  			curUser.send('|pm|' + pmName + '|' + curUser.getIdentity() + '|' + message + from);
  		}
  	});
  };
  
  // format: RPC.messageSeniorStaff('message', 'person')
  //
  // usage: RPC.messageSeniorStaff('Mystifi is a confirmed user and they were banned from a public room. Assess the situation immediately.', '~Server')
  //
  // this makes a PM from ~RPC Server stating the message
  
  
  RPC.pmStaff = function (message, pmName, from) {
  	pmName = (pmName ? pmName : '~Staff PM [DO NOT REPLY]');
  	from = (from ? ' (PM from ' + from + ')' : '');
  	Users.users.forEach(curUser => {
  		if (curUser.isStaff) {
  			curUser.send('|pm|' + pmName + '|' + curUser.getIdentity() + '|' + message + from);
  		}
  	});
  };
  