Skip to content

Instantly share code, notes, and snippets.

@waylaidwanderer
Last active January 14, 2018 10:15
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save waylaidwanderer/6bb095e0731ef41d49dc to your computer and use it in GitHub Desktop.
Save waylaidwanderer/6bb095e0731ef41d49dc to your computer and use it in GitHub Desktop.
Steam Broadcast Chat Cleaner - cleans up the chat of any Steam Broadcast, massively increasing the conversation quality. Works on Dota 2's Watch page as well. Requires Greasemonkey (Firefox) or Tampermonkey (Chrome). Screenshots: http://i.imgur.com/7qEfM9B.png, http://i.imgur.com/PHW4TbK.png - Click "Raw" to install.
// ==UserScript==
// @name Steam Broadcast Chat Cleaner
// @namespace http://jzhang.net/
// @version 3.1.1
// @description Cleans up Steam Broadcast chat
// @author waylaidwanderer
// @include http://steamcommunity.com/broadcast/watch/*
// @require //code.jquery.com/jquery-1.11.3.min.js
// @downloadURL https://gist.github.com/waylaidwanderer/6bb095e0731ef41d49dc/raw/SteamBroadcastChatCleaner.user.js
// @updateURL https://gist.github.com/waylaidwanderer/6bb095e0731ef41d49dc/raw/SteamBroadcastChatCleaner.user.js
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// ==/UserScript==
(function(){
function GM_wait()
{
if (typeof jQuery != 'undefined') {
letsJQuery(jQuery);
} else if (typeof unsafeWindow.jQuery == 'undefined') {
window.setTimeout(GM_wait,100);
}
else {
unsafeWindow.jQuery(function() { letsJQuery(unsafeWindow.jQuery); });
}
}
GM_wait();
function letsJQuery($)
{
var $body = $('body');
var englishOnly = GM_getValue("englishOnly", false);
var noEmoticonsOnly = GM_getValue("noEmoticonsOnly", true);
var noEmoticonsOnlyPercent = GM_getValue("noEmoticonsOnlyPercent", 50);
var noAllCaps = GM_getValue("noAllCaps", true);
var noAllCapsPercent = GM_getValue("noAllCapsPercent", 80);
var noRepeatedText = GM_getValue("noRepeatedText", true);
var noRepeatedTextMaxRepeats = GM_getValue("noRepeatedTextMaxRepeats", 2);
var noShortMessages = GM_getValue("noShortMessages", true);
var noShortMessagesMinLength = GM_getValue("noShortMessagesMinLength", 3);
var noRepeatedMessages = GM_getValue("noRepeatedMessages", true);
var noRepeatedMessagesMaxRepeats = GM_getValue("noRepeatedMessagesMaxRepeats", 1);
var noRepeatedMessagesMuteUser = GM_getValue("noRepeatedMessagesMuteUser", false);
var showMessageRemoved = GM_getValue("showMessageRemoved", false);
var enableWordFilter = GM_getValue("enableWordFilter", false);
var wordFilter = GM_getValue("wordFilter", "").split(",");
$(".EmoticonContainer").after("<div class=\"EmoticonContainer ChatSettings\" style=\"cursor: pointer;\"><span style=\"opacity: 0.4; display: inline-block; height: 22px; width: 22px; margin-top: 4px; vertical-align: middle; background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkJFQ0Q1M0UyOTAwODExRTJCOEQxQjI4RTMyMTI3NEIwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkJFQ0Q1M0UzOTAwODExRTJCOEQxQjI4RTMyMTI3NEIwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QkVDRDUzRTA5MDA4MTFFMkI4RDFCMjhFMzIxMjc0QjAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QkVDRDUzRTE5MDA4MTFFMkI4RDFCMjhFMzIxMjc0QjAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6u3ykzAAAAvUlEQVR42oxTCxHDIAwNDpAwCUiYhEpAQutgDiqhEiqhczAJnYNKSJNb2AENJe/ucXySd+EFADpARE/c8IeDGLMzAIPAjCUOswAFDMQncckVTAKSnMoeZc3zuStAB0GCc6y8X8U1TdvxChb0FoENdQQltut4QmxctVjERvJyY/StaYxPp83/iWbaXpumCtDwkBbVjgfDKy1a9+qZ1qpgFfe9eDGAESzgss8xEb/WZOfcO1UwKlewgP8GnAIMACuKvjkRumvoAAAAAElFTkSuQmCC') no-repeat center;\">&nbsp;</span></div>");
var settingsClass = (window.location != window.parent.location) ? "iframe" : "no-iframe";
$body.append ('\
<div id="chat-settings" class="'+settingsClass+'">\
<form>\
<div class="form-group">\
<input type="checkbox" id="englishOnly"'+(englishOnly ? " checked" : "")+'>\
<label for="englishOnly">Hide Non-English Messages (also hides emoticons which use Unicode characters like <span style=\"display: inline-block;\">( ͡° ͜ʖ ͡°))</span></label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="noEmoticonsOnly"'+(noEmoticonsOnly ? " checked" : "")+'>\
<label for="noEmoticonsOnly">Hide Emoticons-Only Messages</label>\
<br>\
<input type="number" id="noEmoticonsOnlyPercent" min="0" max="100" style="margin: 5px auto 5px 25px;" value="'+noEmoticonsOnlyPercent+'">\
<label for="noEmoticonsOnlyPercent">% of message that has to be emoticons (emoticons count as 1.5 characters)</label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="noAllCaps"'+(noAllCaps ? " checked" : "")+'>\
<label for="noAllCaps">Hide ALL-CAPS Messages</label>\
<br>\
<input type="number" id="noAllCapsPercent" min="0" max="100" style="margin: 5px auto 5px 25px;" value="'+noAllCapsPercent+'">\
<label for="noAllCapsPercent">% of message that has to be ALL CAPS</label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="noRepeatedText"'+(noRepeatedText ? " checked" : "")+'>\
<label for="noRepeatedText">Hide Repeated-Characters/Words Messages</label>\
<br>\
<input type="number" id="noRepeatedTextMaxRepeats" min="1" style="margin: 5px auto 5px 25px;" value="'+noRepeatedTextMaxRepeats+'">\
<label for="noRepeatedTextMaxRepeats">Maximum # of repeated occurrences</label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="noShortMessages"'+(noShortMessages ? " checked" : "")+'>\
<label for="noShortMessages">Hide Short Messages</label>\
<br>\
<input type="number" id="noShortMessagesMinLength" min="0" style="margin: 5px auto 5px 25px;" value="'+noShortMessagesMinLength+'">\
<label for="noShortMessagesMinLength">Minimum # of characters</label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="noRepeatedMessages"'+(noRepeatedMessages ? " checked" : "")+'>\
<label for="noRepeatedMessages">Hide Repeated Messages</label>\
<br>\
<input type="number" id="noRepeatedMessagesMaxRepeats" min="0" style="margin: 5px auto 5px 25px;" value="'+noRepeatedMessagesMaxRepeats+'">\
<label for="noRepeatedMessagesMaxRepeats"># of repeated messages before action</label>\
<br>\
<input type="checkbox" id="noRepeatedMessagesMuteUser" style="margin: 5px auto 5px 25px;"'+(noRepeatedMessagesMuteUser ? " checked" : "")+'>\
<label for="noRepeatedMessagesMuteUser">Also mute user</label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="enableWordFilter"'+(enableWordFilter ? " checked" : "")+'>\
<label for="enableWordFilter">Enable word filter</label>\
<br>\
<input type="text" id="wordFilter" style="margin: 5px auto 5px 25px;" value="'+wordFilter+'">\
<label for="wordFilter">Comma-delimited list of words to filter (case insensitive). Prepend word with * for case-sensitive word (e.g. "*LoL")</label>\
</div>\
<div class="form-group">\
<input type="checkbox" id="showMessageRemoved"'+(showMessageRemoved ? " checked" : "")+'>\
<label for="showMessageRemoved">Show "&lt;message deleted&gt;" instead of hiding message</label>\
</div>\
<div class="buttons">\
<a href="#" class="close ChatSend">Close</a>\
<a href="#" class="save ChatSend">Save</a>\
</div>\
</form>\
</div>\
');
$body.on('mouseenter', '.ChatSettings span', function() {
$(this).css('opacity', 1);
});
$body.on('mouseleave', '.ChatSettings span', function() {
$(this).css('opacity', 0.4);
});
$body.on('click', '#chat-settings .close', function() {
$("#chat-settings").hide();
});
$body.on('click', '#chat-settings .save', function() {
GM_setValue("englishOnly", $("#englishOnly").is(':checked'));
GM_setValue("noEmoticonsOnly", $("#noEmoticonsOnly").is(':checked'));
GM_setValue("noEmoticonsOnlyPercent", $("#noEmoticonsOnlyPercent").val());
GM_setValue("noAllCaps", $("#noAllCaps").is(':checked'));
GM_setValue("noAllCapsPercent", $("#noAllCapsPercent").val());
GM_setValue("noRepeatedText", $("#noRepeatedText").is(':checked'));
GM_setValue("noRepeatedTextMaxRepeats", $("#noRepeatedTextMaxRepeats").val());
GM_setValue("noShortMessages", $("#noShortMessages").is(':checked'));
GM_setValue("noShortMessagesMinLength", $("#noShortMessagesMinLength").val());
GM_setValue("noRepeatedMessages", $("#noRepeatedMessages").is(':checked'));
GM_setValue("noRepeatedMessagesMaxRepeats", $("#noRepeatedMessagesMaxRepeats").val());
GM_setValue("noRepeatedMessagesMuteUser", $("#noRepeatedMessagesMuteUser").is(':checked'));
GM_setValue("showMessageRemoved", $("#showMessageRemoved").is(':checked'));
GM_setValue("enableWordFilter", $("#enableWordFilter").is(':checked'));
GM_setValue("wordFilter", $("#wordFilter").val());
englishOnly = GM_getValue("englishOnly", true);
noEmoticonsOnly = GM_getValue("noEmoticonsOnly", true);
noEmoticonsOnlyPercent = GM_getValue("noEmoticonsOnlyPercent", true);
noAllCaps = GM_getValue("noAllCaps", true);
noAllCapsPercent = GM_getValue("noAllCapsPercent", 80);
noRepeatedText = GM_getValue("noRepeatedText", true);
noRepeatedTextMaxRepeats = GM_getValue("noRepeatedTextMaxRepeats", 2);
noShortMessages = GM_getValue("noShortMessages", true);
noShortMessagesMinLength = GM_getValue("noShortMessagesMinLength", 3);
noRepeatedMessages = GM_getValue("noRepeatedMessages", true);
noRepeatedMessagesMaxRepeats = GM_getValue("noRepeatedMessagesMaxRepeats", 2);
noRepeatedMessagesMuteUser = GM_getValue("noRepeatedMessagesMuteUser", false);
showMessageRemoved = GM_getValue("showMessageRemoved", false);
enableWordFilter = GM_getValue("enableWordFilter", false);
wordFilter = GM_getValue("wordFilter", "").split(",");
$("#chat-settings").hide();
});
$body.on('click', '.ChatSettings', function() {
$("#chat-settings").toggle();
});
$body.on('click', '.message-removed a', function() {
$(this).hide();
$(this).parent().find('span').show();
});
waitForKeyElements("#ChatMessages div.ChatMessage", processMessages);
var repeatedMessages = [];
function processMessages(jNode) {
var remove = false;
var message = jNode.find(".tmplChatMessage").text();
var originalMessage = jNode.find(".tmplChatMessage").html();
message = message.replace(/<img[^>]*>/g,"").trim();
if (noEmoticonsOnly && !remove) {
if (message == "") {
remove = true;
} else {
var div = "<div>" + originalMessage + "</div>";
var numEmoticons = $(div).find('img').length;
if (parseFloat(numEmoticons * 1.5 / (message.length + numEmoticons) * 100) >= noEmoticonsOnlyPercent) {
remove = true;
}
}
}
if (englishOnly && !remove) {
var regex = /[^\x00-\x7F]+/;
if (message.match(regex)) {
remove = true;
}
}
if (noShortMessages && !remove) {
if (message.length < noShortMessagesMinLength) {
remove = true;
}
}
if (noAllCaps && !remove) {
var numCaps = 0;
var tmpMessage = message.replace(/\s+/g, '');
for (var i = 0; i < tmpMessage.length; i++) {
var char = tmpMessage[i];
if (char == char.toUpperCase()) {
numCaps++;
}
}
if (parseFloat(numCaps / tmpMessage.length * 100) >= noAllCapsPercent) {
remove = true;
}
}
if (noRepeatedText && !remove) {
var tmpMessage = message.replace(/\s+/g, '').toLowerCase();
var repeats = [];
var lengthToCheck = 1;
while (lengthToCheck < tmpMessage.length) {
var chunks = chunkString(tmpMessage, lengthToCheck);
for (var i = 0; i < chunks.length; i++) {
var nextChunk = (typeof chunks[i+1] === 'undefined') ? "" : chunks[i+1];
if (chunks[i] == nextChunk) {
var foundRepeat = false;
for (var j = 0; j < repeats.length; j++) {
var repeatData = repeats[j];
if (repeatData.value == chunks[i]) {
repeatData.numRepeats++;
foundRepeat = true;
if (repeatData.numRepeats > noRepeatedTextMaxRepeats) {
remove = true;
}
break;
}
}
if (remove) {
break;
} else if (!foundRepeat) {
var data = {
value: chunks[i],
numRepeats: 1
};
repeats.push(data);
}
}
}
lengthToCheck++;
}
}
if (noRepeatedMessages && !remove) {
var steamId = jNode.attr("data-steamid");
var found = false;
for (var i = 0; i < repeatedMessages.length; i++) {
var user = repeatedMessages[i];
if (!user.muted) {
if (user.steamId == steamId) {
found = true;
if (user.lastMessage == message) {
user.numRepeats++;
if (user.numRepeats > noRepeatedMessagesMaxRepeats) {
if (noRepeatedMessagesMuteUser) {
user.muted = true;
var personaName = jNode.find(".tmplChatName").text();
BroadcastWatch.GetChat().MuteUser(steamId, personaName);
}
remove = true;
}
} else {
user.lastMessage = message;
}
break;
}
}
}
if (!found) {
var data = {
steamId: steamId,
lastMessage: message,
numRepeats: 0,
muted: false
};
repeatedMessages.push(data);
}
}
if (enableWordFilter && !remove) {
var words = message.replace(/[.,-\/#!$%\^&\*;:{}=\-_`~()]/g,'').split(" ");
for (var i = 0; i < words.length; i++) {
var word = words[i].trim();
if (word != "") {
for (var j = 0; j < wordFilter.length; j++) {
var filterWord = wordFilter[j].trim();
var caseSensitive = filterWord.charAt(0) == "*";
if (caseSensitive) {
filterWord = filterWord.replace("*", "");
if (filterWord == word) {
remove = true;
break;
}
} else {
if (filterWord.toLowerCase() == word.toLowerCase()) {
remove = true;
break;
}
}
}
if (remove) {
break;
}
}
}
}
if (remove) {
if (showMessageRemoved) {
jNode.find(".tmplChatMessage").html("<span class=\"message-removed\"><a href=\"#\">&lt;message deleted&gt;</a><span style=\"display: none;\">"+originalMessage+"</span></span>");
} else {
var chat = BroadcastWatch.GetChat();
jNode.remove();
chat.TrimChat();
chat.UpdateScroll();
}
}
}
function chunkString(str, length) {
return str.match(new RegExp('.{1,' + length + '}', 'g'));
}
function waitForKeyElements (
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents ()
.find (selectorTxt);
if (targetNodes && targetNodes.length > 0) {
btargetsFound = true;
/*--- Found target node(s). Go through each and act if they
are new.
*/
targetNodes.each ( function () {
var jThis = $(this);
var alreadyFound = jThis.data ('alreadyFound') || false;
if (!alreadyFound) {
//--- Call the payload function.
var cancelFound = actionFunction (jThis);
if (cancelFound)
btargetsFound = false;
else
jThis.data ('alreadyFound', true);
}
} );
}
else {
btargetsFound = false;
}
//--- Get the timer-control variable for this selector.
var controlObj = waitForKeyElements.controlObj || {};
var controlKey = selectorTxt.replace (/[^\w]/g, "_");
var timeControl = controlObj [controlKey];
//--- Now set or clear the timer as appropriate.
if (btargetsFound && bWaitOnce && timeControl) {
//--- The only condition where we need to clear the timer.
clearInterval (timeControl);
delete controlObj [controlKey]
}
else {
//--- Set a timer, if needed.
if ( ! timeControl) {
timeControl = setInterval ( function () {
waitForKeyElements ( selectorTxt,
actionFunction,
bWaitOnce,
iframeSelector
);
},
0
);
controlObj [controlKey] = timeControl;
}
}
waitForKeyElements.controlObj = controlObj;
}
GM_addStyle("\
#chat-settings {\
display: none;\
border: 3px double black;\
z-index: 777;\
border: 1px solid #4c4c4c;\
background-color: #292929;\
}\
#chat-settings.no-iframe {\
height: 492px;\
width: 476px;\
position: fixed;\
top: 50%;\
left: 50%;\
margin-left:-238px;\
margin-top:-273px;\
padding: 2em;\
}\
#chat-settings.iframe {\
height: 100%;\
position: absolute;\
padding: 1em;\
top: 0;\
bottom: 0;\
left: 0;\
right: 0;\
}\
#chat-settings .form-group {\
padding-bottom: 5px;\
}\
#chat-settings input[type=number] {\
width: 45px;\
}\
#chat-settings .buttons {\
padding-top: 20px;\
}\
#chat-settings .ChatSend {\
padding: 5px;\
}\
" );
}
})();
@waylaidwanderer
Copy link
Author

Still working for future TI6 broadcasts!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment