Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name PM Logs
- // @namespace http://use.i.E.your.homepage/
- // @version 1.2
- // @description Pokemon Showdown! moderator's best friend
- // @include http://play.pokemonshowdown.com/
- // @include https://play.pokemonshowdown.com/
- // @include http://play.pokemonshowdown.com/*
- // @include https://play.pokemonshowdown.com/*
- // @include http://*.psim.us/
- // @include https://*.psim.us/
- // @include http://*.psim.us/*
- // @include https://*.psim.us/*
- // ==/UserScript==
- ///////////////////////////////////////
- // PS PM - LOG SCRIPT by sparkychild //
- ///////////////////////////////////////
- var maxPmLogs = 37500;
- if (!localStorage.pmLogs) {
- localStorage.pmLogs = "[]";
- }
- function packLog(stamp, data) {
- var date = new Date(parseInt(stamp)).toLocaleString();
- return date + "|" + data.from + "|" + data.to + "|" + data.msg;
- }
- // backwards compatability for pm logs
- if (localStorage.pmLogs && localStorage.pmLogs.charAt(0) === "{") {
- var data = JSON.parse(localStorage.pmLogs);
- var newData = {};
- // convert to a new format
- for (var user in data) {
- for (var stamp in data[user]) {
- newData[stamp] = packLog(stamp, data[user][stamp]);
- }
- }
- var blob = Object.keys(newData).sort().map(d => newData[d]);
- localStorage.setItem("pmLogs", JSON.stringify(blob));
- }
- var pmLogs = JSON.parse(localStorage.pmLogs);
- if (maxPmLogs && pmLogs.length > maxPmLogs) pmLogs = pmLogs.slice(-maxPmLogs);
- function toId(s) {
- return "" + (s && typeof s === "string" ? s : "").toLowerCase().replace(/[^a-z0-9]+/g, "");
- }
- function escapeHTML(str) {
- return ('' + str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
- }
- class CmdLine {
- constructor(cmd) {
- this.matches = [];
- let matches = cmd.match(/\-[^\s]+(?:\s(?!\-)(?:(\"|\')(?:(?!\1).)+\1|[^\s]+))?/g);
- if (matches) {
- let cmds = [];
- for (let i = 0; i < matches.length; i++) {
- let [command, ...values] = matches[i].trim().split(" ");
- cmds.push({
- cmd: command.slice(1),
- value: values && values.length ? values.join(" ").replace(/(^["']|['"]$)/g, "") : "",
- });
- }
- this.matches = cmds;
- }
- }
- // find all matches with the command
- find(nonEmpty, ...types) {
- if (typeof nonEmpty !== 'boolean') {
- types = [nonEmpty, ...types];
- nonEmpty = false;
- }
- let results = this.matches
- .filter(t => types.includes(t.cmd) && (!nonEmpty || t.value))
- .map(t => t.value);
- return results;
- }
- // require the command to have a value
- findValues(...types) {
- return this.find(true, ...types);
- }
- }
- function verifyRegex(regex) {
- try {
- new RegExp(regex, 'i');
- return regex;
- } catch (e) {
- return escapeRegex(regex);
- }
- }
- function escapeRegex(str) {
- return (str && typeof str === 'string' ? str : '').replace(/[\\.+*?!=()|[\]{}^$#<>]/g, '\\$&');
- }
- function toUserId(text) {
- if (typeof text !== 'string' && typeof text !== 'number') return '';
- return '^\\W?' + ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '').replace(/[a-z0-9]/g, '$&[^a-zA-Z0-9]*') + '$';
- }
- function runPmLogs(query) {
- if (!pmLogs.length) return {logs: null};
- var directory = pmLogs.slice(0);
- if (!query) {
- return {lines: 100, logs: directory.slice(-100).reverse()};
- }
- let parser = new CmdLine(query);
- // find values for user.
- var userRegex = parser.findValues('ur', 'userregex').map(e => verifyRegex(e));
- var userPlain = parser.findValues('u', 'user').map(e => escapeRegex(e));
- var userId = parser.findValues('userid', 'ui').map(e => toUserId(e));
- var userSearch = parser.findValues('ur', 'userregex', 'u', 'user', 'userid', 'ui'); // display purposes
- // values for message contents
- var messageRegex = parser.findValues('mr', 'messageregex', 'msgregex').map(e => verifyRegex(e));
- var messagePlain = parser.findValues('m', 'message', 'msg').map(e => escapeRegex(e));
- var messageSearch = parser.findValues('m', 'message', 'msg', 'mr', 'messageregex', 'msgregex'); // display purposes
- // values for date
- var dateRegex = parser.findValues('dr', 'dateregex').map(e => verifyRegex(e));
- var datePlain = parser.findValues('d', 'date').map(e => e === 'today' ? new Date().toLocaleDateString() : escapeRegex(e));
- var dateSearch = parser.findValues('dr', 'dateregex', 'd', 'date');
- // values for lines
- var lines = parser.findValues('l', 'lines', 't', 'tail');
- // values for reverse
- var reverse = !parser.find('r', 'reverse').length;
- // all lines
- var all = parser.find('a', 'all').length;
- var complexSearch = !!userSearch || !!dateSearch || !!messageSearch;
- var message = !!messageSearch ? new RegExp('(' + [...messagePlain, ...messageRegex].join('|') + ')', 'i') : null;
- var date = !!dateSearch ? new RegExp('(' + [...datePlain, ...dateRegex].join('|') + ')', 'i') : null;
- var user = !!userSearch ? new RegExp('(' + [...userPlain, ...userId, ...userRegex].join('|') + ')', 'i') : null;
- // run the actual search
- directory = directory.filter(l => {
- var p = l.split("|");
- var d = p[0];
- var u = p[1];
- var u2 = p[2];
- var m = p.slice(3).join("|");
- return !/\/(raw|html)\s.+?\<(p|br)/i.test(m) && (!(message && !message.test(m)) && !(date && !date.test(d)) && !(user && !user.test(u) && !user.test(u2)));
- });
- var maxLength = directory.length;
- lines = all ? maxLength : (parseInt(lines[0]) || 100);
- if (lines > maxLength) lines = maxLength;
- directory = directory.slice(-lines);
- if (reverse) directory = directory.reverse();
- if (!directory.length) return {userSearch, dateSearch, messageSearch, reverse, lines, logs: null};
- return {complexSearch, userSearch, dateSearch, messageSearch, reverse, lines, logs: directory};
- }
- function formatPmLogs(data) {
- let title = data.complexSearch ? "<details><summary>Search Criteria: </summary>User: " + escapeHTML(data.userSearch.join(", ")) + "<br />Date: " + escapeHTML(data.dateSearch.join(', ')) + "<br />Message: " + escapeHTML(data.messageSearch.join(', ')) + "<br />Lines: " + data.lines + "<br />Reverse: " + !data.reverse + "</details><br />" : !data.logs ? '' : "Last " + data.lines + " lines:<br />";
- let logs = !data.logs ? 'No Logs Found.' :
- data.logs.map(l => {
- var line = l.split("|");
- let pm = line.slice(3).join("|");
- if (pm.indexOf("/raw") === 0 || pm.indexOf("/html") === 0) {
- pm = pm.replace(/^\/html\s/i, "");
- } else if (pm.indexOf("/error") === 0) {
- pm = "<font color=red>" + escapeHTML(pm.slice(6)) + "</font>";
- } else {
- pm = BattleLog.parseMessage(pm);
- }
- return "<p><font color=slategray>[" + line[0] + "]</font> <b><font color=" + BattleLog.usernameColor(toId(line[1])) + ">" + line[1].trim() + ":</font></b> <font color=slategray>(Private to <font color=" + BattleLog.usernameColor(toId(line[2])) + ">" + line[2].trim() + "</font>)</font> " + pm + "</p>";
- }).join("");
- return {title, logs};
- }
- function renderNewTab(name, contents) {
- // >view-[name]
- var buffer = `>view-${toId(name)}\n|init|html\n|title|${name}\n|pagehtml|${contents}`;
- unsafeWindow.app.socket.onmessage({data: buffer});
- }
- try {
- function main() {
- probeInterval = setInterval(function () {
- if (unsafeWindow.app && unsafeWindow.app.socket && unsafeWindow.app.socket.onmessage &&
- unsafeWindow.app.socket.onmessage.toString().indexOf("self.receive") >= 0) {
- clearInterval(probeInterval);
- unsafeWindow.app.socket._onmessage = unsafeWindow.app.socket.onmessage;
- unsafeWindow.app.socket.onmessage = function (msg) {
- receive(msg.data);
- return this._onmessage(msg);
- };
- unsafeWindow.app.socket._send = unsafeWindow.app.socket.send;
- unsafeWindow.app.socket.send = function (msg) {
- if (!msg) return;
- if (msg.indexOf("|/") >= 0 && (msg.indexOf("/") - msg.indexOf("|") === 1) && (msg.indexOf("|/") !== msg.indexOf("|//"))) {
- if (!msg.split("|/")[1]) return;
- var tCmd = toId(msg.split("|/")[1].split(" ")[0]);
- var roomid = (msg.split("|")[0] || "lobby").toLowerCase().replace(/[^a-z0-9\-]/g, "");
- var room = unsafeWindow.app.rooms[(msg.split("|")[0].replace(/[^a-z0-9\-]+/g, "") || "lobby")];
- var arg = msg.split("|/").slice(1).join("|/").split(" ").slice(1).join(" ") || " ";
- switch (tCmd) {
- case "js":
- room.receive(">> " + arg);
- try {
- var result = eval(arg);
- room.receive("<< " + JSON.stringify(result));
- } catch (e) {
- room.receive("<< ERROR!");
- room.receive(e.stack);
- }
- return;
- break;
- case "help":
- var buffer = [];
- if (toId(arg) === "js") {
- buffer.push("/js [code] - runs arbitrary code.");
- }
- if (toId(arg) === "convo") {
- buffer.push("/convo [userid] - Loads up all PM logs with that user.");
- }
- if (toId(arg) === "pmsearch") {
- buffer.push("/pmsearch [string] - Loads up all PM logs with that string in the message.");
- }
- if (toId(arg) === "pmlogs") {
- return unsafeWindow.app.socket._onmessage({
- data: "|popup||html|" +
- '<b>How to search logs using the /pmlogs command:</b><br /><br /><center><b>Searching content</b></center><div style="border: 2px solid; border-color: brown; background-color: cornsilk; border-radius: 10px; padding: 8px;"><u>The <b>-user (-u, -ur, -ui)</b> tag</u><br />• <code>-u [target]</code> searches for a simple username as either the sender or the reciever.<br />• <code>-ur [target]</code> uses a regex search for a pattern in the username.<br />• <code>-ui [target]</code> checks for logs with that userid, regardless of formatting in the name.<br /><br /><u>The <b>-message (-m, -mr)</b> tag</u><br />• <code>-m [target]</code> searches for a phrase in the message.<br />• <code>-mr [target]</code> uses a regex search for a pattern in the message.<br /><br />' +
- '<u>The <b>-date (-d, -dr)</b> tag</u><br />• <code>-d [target]</code> searches for a phrase in the date.<br />• <code>-dr [target]</code> uses a regex search for a pattern in the date.<br /><br />Add an extra <b>r</b> after the command (<b>-u</b> becomes <b>-ur</b>) to make it accept regex. If you don't know what you're doing DO NOT use this. ReDoS can crash the browser session, or potentially do even worse.<br />To do a search with spaces in it, put the search phrase around <code>"</code>\'s or <code>\'<code>\'s.</div>' +
- '<br /><center><b>Searching content</b></center><div style="border: 2px solid; border-color: brown; background-color: cornsilk; border-radius: 10px; padding: 8px;"><u>The <b>-tail (-t)</b> and the <b>-all (-a)</b> tag</u><br />• <code>-tail [# of lines]</code> to control how many lines the popup will show. Default is the most recent 100.<br />• <code>-all</code> shows every single line that matches the search<br /><br/><u>The <b>-reverse (-r)</b> tag</u><br />• <code>-r</code> shows the logs in chronological order instead of the newest at the top.</div>' +
- '<br /><center><b>Application</b></center><div style="border: 2px solid; border-color: brown; background-color: cornsilk; border-radius: 10px; padding: 8px;">To put it all together, put the arguments one after the other.<br /><br />Example:<br />• A search of <code>-ur sparky(?!child)(.+?) -d 2016 -mr \b[a-z]+?urry\b</code> would search for a user with the word sparky but not sparkychild, the log happening in 2016, containing a word that ends in "urry", such as furry.<br />• A search of <code>-ui trickster -ui eyan</code> would search for users with the userids "trickster" or "eyan". Matches would include "T♥r♥i♥c♥k♥s♥t♥e♥r" and "E書y呆a子n".</div>'
- });
- }
- if (!arg || !toId(arg)) {
- room.receive("|raw|<b>Client commands:</b>");
- room.receive("|raw|/js, /pmlogs, /convo, /pmsearch");
- room.receive("|raw|<b>Server commands:</b>");
- }
- if (!arg || !toId(arg)) room.receive("|raw|<b>Server commands:</b>");
- if (buffer.length > 0) {
- room.receive("|raw|" + buffer.join("<br>"));
- }
- if (toId(arg) && buffer.length > 0) return;
- break;
- case "convo":
- return this.send("|/pmlogs -a -ui " + arg);
- break;
- case "pmsearch":
- return this.send("|/pmlogs -a -m " + arg);
- break;
- case "pmlogs":
- Promise.resolve(
- runPmLogs(arg)
- )
- .then(res => {
- Promise.resolve(
- formatPmLogs(res)
- )
- .then(res => {
- var display = "<center><h3>PM Logs</h3></center><div style='padding-left: 10px; padding-right: 10px; word-wrap: break-word;'>" + res.title + res.logs + "</div>";
- renderNewTab('PM Logs', display);
- });
- })
- .catch(err => {
- room.receive('Oops, something failed while formatting the logs.');
- room.receive('|raw|<div style="color: red">' + err.stack + '</div>');
- })
- .catch(err => {
- room.receive('Oops, something failed while running the search.');
- room.receive('|raw|<div style="color: red">' + err.stack + '</div>');
- });
- return;
- }
- }
- return this._send(msg);
- };
- }
- }, 0);
- }
- function receive(entry) {
- var room = "lobby";
- messages = entry.split("\n");
- for (var i = 0; i < messages.length; i++) {
- if (messages[i].indexOf("|pm|") === 0) parsePM(messages[i]);
- }
- }
- function parsePM(message) {
- var parts = message.split("|");
- var date = Date.now();
- pmLogs.push(packLog(date, {
- from: parts[2],
- to: parts[3],
- msg: parts.slice(4).join("|")
- }));
- localStorage.setItem("pmLogs", JSON.stringify(pmLogs));
- }
- setTimeout(function () {
- main();
- }, 2000);
- } catch (e) {
- console.log(e);
- }
- // ==/UserScript==
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement