NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Picarto Chat Extender // @namespace http://www.furaffinity.net/user/Maloo // @description Adds in additional interfaces for the Picarto chat script for userscripts to use. Add "https://picarto.tv/js/chat/chat_new_client.min.js$~xmlhttprequest" to your adblocker if you are not running Firefox/Greasemonkey. // @license MIT // @include https://picarto.tv/* // @exclude https://picarto.tv/communities* // @exclude https://picarto.tv/ // @exclude https://picarto.tv/settings* // @version 1.4 // @grant none // @run-at document-start // ==/UserScript== var verString = "1.5 (2020-04-03)"; /* Known Script Hashes, with dates observed and variables: E71D7DF4: (9/17/17) e/G/c CA9ABC7C: (9/25/17) e/q/l E3ACCB3A: (10/11/17) e/q/l F12DDC6B: (10/23/17) e/W/l 5C7AFE02: (10/28/17) e/q/l 7B66DC7C: (11/20/17) e/q/l BF9BAEE9: (12/4/17) externalScope/socket/statusMsg (no longer minified? RegExes hopefully expanded to cover both minified and unminified) 7C151ACA: (1/4/18) e/G/l 06FFD550: (5/8/18) e/V/c */ var i; var changed = 1; // How many scripts need to be edited with if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1){ // On firefox, we can block the script. For now. changed = 1; // How many scripts need to be edited with window.addEventListener('beforescriptexecute', function(e) { ///for external script: var src = e.target.src; if (src.search(/chat_new_client\.min\.js/) != -1) { changed--; e.preventDefault(); e.stopPropagation(); console.log("Blocked Chat Script"); } if(changed === 0) window.removeEventListener(e.type, arguments.callee, true); }, true); //////////////////////////////////////////////// } else console.log ("Not Firefox, make sure to block chat script w/Adblock! \"https://picarto.tv/js/chat/chat_new_client.min.js$~xmlhttprequest\""); function exec(fn) { var script = document.createElement('script'); script.setAttribute("type", "application/javascript"); script.textContent = '(' + fn + ')();'; modifyHeadSafe(function () {document.head.appendChild(script);}); // run the script modifyHeadSafe(function () {document.head.removeChild(script);}); // clean up } function passStrVarToWindow(toPass, varName) { var script = document.createElement('script'); script.setAttribute("type", "application/javascript"); script.textContent = varName + ' = "' + toPass + '";'; modifyHeadSafe(function () {document.head.appendChild(script);}); // run the script modifyHeadSafe(function () {document.head.removeChild(script);}); // clean up } //Add permanent window scope script. Now with less giant strings without syntax highlighting! function addWindowScript(fn, ident) { var script = document.createElement('script'); script.setAttribute("type", "application/javascript"); script.setAttribute("id", ident); script.textContent = fn.toString().slice(fn.toString().indexOf("{") + 1,-2); //KLUUUUUUDGE. modifyHeadSafe(function () {document.head.appendChild(script)}); } var headFunctions = []; function modifyHeadSafe(toDo){ if ((document.head) && (headFunctions.length == 0)) { if (toDo != null) toDo(); } else { //console.log("Pushing headFunction #" + headFunctions.length); headFunctions.push(toDo); } } var waitForHead = setInterval(function() { if (document.head) { while (headFunctions.length != 0) { //console.log("Executing headFunction #" + headFunctions.length); headFunctions.shift()(); } clearInterval(waitForHead); } },50); //Create container for holding functions and messages to be executed after script load addWindowScript(function() { window.CustomFunctions = { callbacks:[], addMsgBacklog:[], execute: function(){ for (i = 0; i < this.callbacks.length; i++) { this.callbacks[i](); } var tsTemp = window.addMsg.toString(); if (tsTemp.search(/CustomFunctions/) == -1) { for (i = 0; i < this.addMsgBacklog.length; i++) { window.addMsg(this.addMsgBacklog[i]); } } else console.log("Recursion Protection!"); } }; }, "PicChatExt_CF"); //Temporary Function to pass messages into backlog. exec(function() { window.addMsg = function(msg) { console.log("\"" + msg + "\" added to backlog."); CustomFunctions.addMsgBacklog.push(msg); }; }); exec(function() { if (!window.initChat) { window.initChat = function() { console.log("initChat Error Supressed"); }; } }); passStrVarToWindow(verString, "verString"); addWindowScript(function () { String.prototype.hashCode = function(){ var hash = 0; if (this.length === 0) return hash; for (i = 0; i < this.length; i++) { var char = this.charCodeAt(i); hash = ((hash<<5)-hash)+char; hash = (hash & hash) >>> 0; // Convert to 32bit UNSIGNED integer } return hash; }; function hexRep(number, width) { return (number).toString(16).slice(-width).toUpperCase(); } var jsContent; //console.log("Borp1"); function reqListener () { var scriptHash = hexRep(this.responseText.hashCode(), 8); console.log("Recieved Chat JS. Hash: " + scriptHash ); jsContent = this.responseText; var tempreg = /function\s*\(/; var temp = tempreg.exec(jsContent); var exScopeVar = jsContent.substring(temp.index + temp[0].length, jsContent.search(/\)\{/)); //console.log(temp); window.DEBUGtemp = temp; var inSocktVar = jsContent.substring(jsContent.search(/\w+\s?=\s?new picarto\.Client/), jsContent.search(/\s?=\s?new picarto\.Client\([^)]+\)/)); var tempLoc = jsContent.search(/\s?=\s?new picarto\.Client\([^)]+\)/); var crSocktStr = jsContent.substring(tempLoc, tempLoc + jsContent.match(/\s?=\s?new picarto\.Client\([^)]+\)/)[0].length); var statusMsgF = jsContent.substring(jsContent.search(/\w+\(\w+\)\s?\{\s*\w+\(['"]<div><span class=(\\'|'|\\"|")update.*(\\'|'|\\"|")>['"]\s*\+/), jsContent.search(/\(\w+\)\s?\{\s*\w+\(['"]<div><span class=(\\'|'|\\"|")update.*(\\'|'|\\"|")>['"]\s*\+/)); if ((exScopeVar.length < 1) || (inSocktVar.length < 1) || (statusMsgF.length < 1)) {console.log("Variables not isolated successfully!"); console.log("exScopeVar: " + exScopeVar + " inSocktVar: " + inSocktVar + " statusMsgF: " + statusMsgF); return;} jsContent = jsContent.replace(/[;,]\s?\w+(\.|\['|\[")socket"?'?\]?\s?=\s?\w+[,;]/, ',' + exScopeVar + '.addMsg = ' + statusMsgF + ','); console.log("exScopeVar: " + exScopeVar + " inSocktVar: " + inSocktVar + " statusMsgF: " + statusMsgF); jsContent = jsContent.replace(/\w+\s?=\s?new picarto\.Client\([^)]+\)/, inSocktVar + crSocktStr); tempreg = new RegExp("[;,]\s*" + inSocktVar + "\.Connect\(\)"); tempLoc = jsContent.search(tempreg); jsContent = jsContent.slice(0, tempLoc) + ';' + exScopeVar + '.socket = ' + inSocktVar + jsContent.slice(tempLoc); //console.log("Borp5"); var waitFor = setInterval(function() { if (document.querySelector("#channel_chat > audio + script, .panel_popoutchat > audio + script") && window.addMsg) { //console.log("Dad Found!"); //console.log(window.addMsg); clearInterval(waitFor); if (typeof window.socket !== 'undefined') { window.alert("===Picarto Chat Extender===\nChat Script was not blocked.\nThis userscript downloads and modifies\nthe chat script dynamically.\nIf you are not using Firefox,\nyou must use Adblock to block the script\nwith the following filter\n\nhttps://picarto.tv/js/chat/chat_new_client.min.js$~xmlhttprequest"); throw new Error("Chat Script not blocked!"); } var initContent; var iscript = document.createElement('script'); var cscript = document.createElement('script'); initContent = document.querySelector("#channel_chat > audio + script, .panel_popoutchat > audio + script"); cscript.setAttribute("type", "application/javascript"); cscript.setAttribute("id", "ChatClient"); iscript.setAttribute("type", "application/javascript"); iscript.setAttribute("id", "ChatInit"); cscript.textContent = jsContent; iscript.textContent = initContent.innerHTML; initContent.parentNode.removeChild(initContent); document.head.appendChild(cscript); document.head.appendChild(iscript); window.initContent = initContent; console.log("Chat Extender Script Loaded."); setTimeout(function() { window.addMsg("Chat Extender Ver " + verString + " Loaded Successfully.\nChat Script Hash: " + scriptHash); window.CustomFunctions.execute(); }, 1000); }}, 100); //console.log("TimeoutAdded"); } var oReq = new XMLHttpRequest(); oReq.addEventListener("load", reqListener); oReq.open("GET", "https://picarto.tv/js/chat/chat_new_client.min.js"); oReq.send(); //console.log("Borp2"); }, "PicChatExt_RepScript");