NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name redrobins.interactive // @namespace http://reddit.com/r/redrobins // @version 1.31 // @description Propaganda Bot that is also interactive // @author (Credit to /u/GuitarShirt and /u/keythkatz for original robinautovoter.js) // @match https://www.reddit.com/robin* // @updateURL https://openuserjs.org/install/4U6U57/Robin_Autovoter.user.js // @grant GM_getValue // @grant GM_setValue // @require https://cdnjs.cloudflare.com/ajax/libs/jQuery-linkify/1.1.7/jquery.linkify.js // @require https://cdnjs.cloudflare.com/ajax/libs/jshashes/1.0.5/hashes.min.js // ==/UserScript== /* jshint esnext: true */ function sendMessage(message){ $("#robinSendMessage > input[type='text']").val(message); $("#robinSendMessage > input[type='submit']").click(); } function sendTrackingStatistics(config) { if(!GM_getValue("stat-tracking",true)) { return; } // Use the name / id from the passed config if available // Otherwise fallback to the baked info room_name = r.config.robin_room_name; room_id = r.config.robin_room_id; if('undefined' !== typeof config['robin_room_name']) { room_name = config.robin_room_name; } if('undefined' !== typeof config['robin_room_id']) { room_id = config.robin_room_id; } trackers = [ "https://jrwr.space/robin/track.php", "https://monstrouspeace.com/robintracker/track.php" ]; queryString = "?id=" + room_name.substr(0,10) + "&guid=" + room_id + "&ab=" + r.robin.stats.abandonVotes + "&st=" + r.robin.stats.continueVotes + "&gr=" + r.robin.stats.increaseVotes + "&nv=" + r.robin.stats.abstainVotes + "&count=" + r.robin.stats.totalUsers + "&ft=" + Math.floor(r.config.robin_room_date / 1000) + "&rt=" + Math.floor(r.config.robin_room_reap_time / 1000); trackers.forEach(function(tracker){ $.get(tracker + queryString); }); } function updateStatistics(config) { // Take over r.robin.stats for this if('undefined' === typeof r.robin['stats']) { r.robin.stats = {}; } // Update the userlist if('undefined' !== typeof config['robin_user_list']) { var robinUserList = config.robin_user_list; r.robin.stats.totalUsers = robinUserList.length; r.robin.stats.increaseVotes = robinUserList.filter(function(voter){return voter.vote === "INCREASE";}).length; r.robin.stats.abandonVotes = robinUserList.filter(function(voter){return voter.vote === "ABANDON";}).length; r.robin.stats.abstainVotes = robinUserList.filter(function(voter){return voter.vote === "NOVOTE";}).length; r.robin.stats.continueVotes = robinUserList.filter(function(voter){return voter.vote === "CONTINUE";}).length; r.robin.stats.abstainPct = (100 * r.robin.stats.abstainVotes / r.robin.stats.totalUsers).toFixed(2); r.robin.stats.increasePct = (100 * r.robin.stats.increaseVotes / r.robin.stats.totalUsers).toFixed(2); r.robin.stats.abandonPct = (100 * r.robin.stats.abandonVotes / r.robin.stats.totalUsers).toFixed(2); r.robin.stats.continuePct = (100 * r.robin.stats.continueVotes / r.robin.stats.totalUsers).toFixed(2); // Update the div with that data $('#totalUsers').html(r.robin.stats.totalUsers); $('#increaseVotes').html(r.robin.stats.increaseVotes); $('#continueVotes').html(r.robin.stats.continueVotes); $('#abandonVotes').html(r.robin.stats.abandonVotes); $('#abstainVotes').html(r.robin.stats.abstainVotes); $('#increasePct').html("(" + r.robin.stats.increasePct + "%)"); $('#continuePct').html("(" + r.robin.stats.continuePct + "%)"); $('#abandonPct').html("(" + r.robin.stats.abandonPct + "%)"); $('#abstainPct').html("(" + r.robin.stats.abstainPct + "%)"); } sendTrackingStatistics(config); } // This grabs us the same data that is available in r.config via // parsing down a new page (giving us updated data without us having // to follow it like we probably should) function parseStatistics(data) { // Setup the recursion at the top so we can just return // at failtime. setTimeout(generateStatisticsQuery, 60 * 1000); // There is a call to r.setup in the robin HTML. We're going to try to grab that. // Wish us luck! var START_TOKEN = "<script type=\"text/javascript\" id=\"config\">r.setup("; var END_TOKEN = ")</script>"; // If we can't locate the start token, don't bother to update this. // We'll try again in 60 seconds var index = data.indexOf(START_TOKEN); if(index == -1) { return; } data = data.substring(index + START_TOKEN.length); index = data.indexOf(END_TOKEN); if(index == -1) { return; } data = data.substring(0,index); // This will throw on failure var config = JSON.parse(data); updateStatistics(config); } function generateStatisticsQuery() { // Query for the userlist $.get("/robin",parseStatistics); } function getTimeUntilReap() { var currentTime = Math.floor(Date.now() / 1000); var reapTime = Math.floor(r.config.robin_room_reap_time / 1000); var dT = Math.abs(reapTime - currentTime); var minutes = Math.floor(dT/60); var seconds = "0" + (dT - (minutes * 60)); seconds = seconds.substr(seconds.length-2); // 0 pad the seconds // If we've passed the reap time, put a - in the front. if(reapTime < currentTime) { minutes = "-" + minutes; } return "" + minutes + "m" + seconds + "s"; } function updateReapTimer() { setTimeout(updateReapTimer,1000); $('#reapTimerTime').html(getTimeUntilReap()); } function newMessageHandler(records) { records.forEach(function(record) { var msg = $(record.addedNodes); if(0 === record.addedNodes.length) { return; } timestamp = $(msg[0]).children('.robin-message--timestamp').text(); user = $(msg[0]).children('.robin-message--from').text(); msgText = $(msg[0]).children('.robin-message--message').text(); if(GM_getValue('remove-votemotes',true)) { if(-1 != ["voted to GROW","voted to STAY","voted to ABANDON"].indexOf(msgText)) { $(msg[0]).remove(); return; } } if(GM_getValue('remove-botspam',true)) { // Remove old autovoter spam (SORRY!) AUTOVOTER_TOKEN = '[Robin Autovoter'; if(msgText.substr(0,AUTOVOTER_TOKEN.length)==AUTOVOTER_TOKEN) { $(msg[0]).remove(); return; } } if(GM_getValue('remove-duplicate-messages',true)) { // Make sure the hash list exists if('undefined' === typeof r.robin['msgHashes']) { r.robin.msgHashes = {}; r.robin.MD5 = new Hashes.MD5; } // Hash the message. hash = r.robin.MD5.hex(msgText); // Does the message match our existing hash list? matched = 'undefined' !== typeof r.robin.msgHashes[hash]; // Add it to the list or update the timestamp r.robin.msgHashes[hash] = Math.floor(Date.now()/1000); if(matched) { // Delete it $(msg[0]).remove(); return; } } // Linkify the messages going by $(msg[0]).children('.robin-message--message').linkify(); if(GM_getValue('highlights',true)) { if(!!Notification || Notification.permission === "granted") { if(msgText.toLowerCase().indexOf(r.config.logged.toLowerCase())!=-1) { $(msg[0]).css('background-color','rgba(250, 202, 222, 0.32)'); var n = new Notification('Robin Chat',{ icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAABGdBTUEAALGPC/xhBQAABJdJREFUaAXtWW1oHEUYnnfvcrvXuyRUW6LU1h9+FcQPLJo7S9v8EDFik2vCIbV+BIrBP4IIFsUip7Sg9Y+aojYt+KNihUhio1IqFmswTVNQS2mFiggi/gh+FNI2t7O3O6/vXlk50+3t7F4+tjgLyczuPDPv+zzvzOy7c4ypSymgFFAKKAWUAkoBpYBSQCmgFFAKKAWUAv8/BeBqoYz9/U3mqamnGLInGOBtwCBDvv9Gf99oCPtSk59+J8PlqiCM+eI1JlqHGOJ9fqSIBDIGg/pdbc/C4GDFD+M9iz1hRAQz3/01UdrgOX3lEsaA4ffEvqwBO5MyMofh6IE/a/HJ2ps41q1892NyZF3vcT2RXV+tMXBMPvMm1V9y770r9oSJwDbP2aCS1rVgwM6SQF8wSO1JTwz9PLtPrAlXcj1rbbTvnO20/z1M6OnUg3B06IJ/+6WnsSYsmLO1nvNeGwDjLKn1BZF18ZrXKW4lbuxfQtO5KOMX7bw7jG9HfpLBxjbC5h9TtFlhNogERXdYPz66IwjntceOMHZuaeHnzr9OZJ+hCAdehFkZCKoBxOo9zPOFgkCxm3bZFTU+1q26SYee1ltk1q87UCwijOt6rucVZ0AI0VuXnU8jRRhsy76DmiZ8mi97tKiblptFlXNdT3PL+ZHqocl6bARim1cPKhctwmZ77y2UMu51syikf41clE62yPZfcMLYUUpa/OQLiJVXiKch62g9HAKbqdde27aghK32whpe/mEfRfTuWicarWtM+112jAUhTJ93aS6sVwXg87RWE7LOyeKSjvGLNFYWGBVXyW/awJFTVNnNDS5VXxfog+EkTB6Y8m30eThvEa4mEH9f2OUIp999dfjYnpNHCPB5mIHmhbCZ3/SIee78++SIdAIRxmkPCwAOS4gPvXuZck4J47rictPib6NwNssYnwPMB8b46Nkw48wZYZ7rfpTI7ibjy8I4EBVL0S3rkCqF7d8wYewoXsfL1nuU7RTCGm8Q/zJMDEm/jjxbDW0mdN70JBF9C5Et9QZciJJ25k+MyYNS38qz/YkUYezouYGbzh5H4MOzB5zve5rKR/Slqx6Paif0x0M5X9jKy85pSiAWg+yovrytCw4N8KiEpaf0pajae2n6PhTVWNR+7mkkpaNvGJ33bIdSSUQdx+0nRbicK/SBu1YZtjZiLFpfGEsmtBebjo1Ife8G2ahLGNs3t3GYoajiRncgAk9TZjNAta8AoQk0kaU1lRWCZSkIWUQtQ8IsIah7ANdM+JUIeBOllKuCHKltp3Mqm2x8piUSu1Ljw8dr2xqtX5Ewby/0CibcbGlZ9RiUwbt6RtsJR0b+CmsUHyi22het222Gq0moW2mmrCDD11Z3dyJHU3aaBJrWGJ4RCTZuZFpPwJf7L4a1I4O/jDCu7Wq2HHiHXjd9bupGgP0pLVGCY8O/ygwYd8x/CFv399wrHPtjBnAjqf8RaKnXDJ+fK+JOqp5//76HzXzhOSK7k04PhiCpdaYlD7brDR7HtiphnuvajiiadR1Ww9hB90dmdSkFlAJKAaWAUkApoBRQCigFQirwD/s1iSsutDEJAAAAAElFTkSuQmCC', body: user + ': ' + msgText, }); } } } // DOOT HERE if(msgText.startsWith(".")) { if(msgText.startsWith(".test")) { sendMessage("Red Robins wants YOU! http://reddit.com/r/RedRobins"); } } }); } function addSetting(name,description,initialValue) { currentValue = GM_getValue(name,initialValue); $("#robinDesktopNotifier").append("<label><input type='checkbox' name='robin-" + name + "' " + (currentValue?"checked":"") + ">" + description + "</input></label>"); $("input[name='robin-" + name + "']").on("change",function() { GM_setValue(name,$(this).is(":checked")); }); } // Quit stay-ed groups so we can rejoin function quitStayChat() { // Check back in 60 seconds setInterval(quitStayChat, 60 * 1000); if(!GM_getValue("auto-quit-stay",true)) { return; } if($("#robinQuitWidget").css("display") != "none"){ $("button.robin-chat--quit").click(); } } function listenForSubmit() { var $messageBox = $("#robinSendMessage > input[type='text']"); $messageBox.on( "keypress", function(e) { if (e.which !== 13) return; var message = $messageBox.val(); if (GM_getValue("fast-clear",true) && message === "/clear") { e.preventDefault(); $messageBox.val(''); $("#robinChatMessageList").empty(); } }); } (function(){ // The first thing we do is make sure everything's alright // Reload page on 503 if(document.querySelectorAll("img[src='//www.redditstatic.com/trouble-afoot.jpg']").length > 0) window.location.reload(); // Rejoin room on fail if(document.querySelectorAll("button.robin-home--thebutton").length > 0){ $("#joinRobinContainer").click(); setTimeout(function(){ $("button.robin-home--thebutton").click(); }, 1000); } // Quit stay-ed chats setInterval(quitStayChat, 60 * 1000); // The second thing we do is setup a timer to reload the page. // If the above two lines don't save us, at least we'll reload before // the timer's up // 16 minutes after we join (halfway to max): reload the page setTimeout(function(){ window.location.reload(); }, 16 * 60 * 1000); // Insert the statistics widget if($('#robinStatusWidget').length === 0) { // TODO: This needs some stylesheet love $("#robinDesktopNotifier").after( // Statistics Widget "<div id='robinStatusWidget' class='robin-chat--sidebar-widget'>" + "<table style='font-size: 14px;'>" + "<tr>" + "<td style='padding-right: 3px;'>Total</td>" + "<td id='totalUsers'></td>" + "<td></td>" + "</tr>" + "<tr>" + "<td class='robin--vote-class--increase'><span class='robin--icon'></span></td>" + "<td id='increaseVotes'></td>" + "<td id='increasePct'></td>" + "</tr>" + "<tr>" + "<td class='robin--vote-class--continue'><span class='robin--icon'></span></td>" + "<td id='continueVotes'></td>" + "<td id='continuePct'></td>" + "</tr>" + "<tr>" + "<td class='robin--vote-class--abandon'><span class='robin--icon'></span></td>" + "<td id='abandonVotes'></td>" + "<td id='abandonPct'></td>" + "</tr>" + "<tr>" + "<td class='robin--vote-class--novote'><span class='robin--icon'></span></td>" + "<td id='abstainVotes'></td>" + "<td id='abstainPct'></td>" + "</tr>" + "</table>" + "</div>" + // Reap timer widget "<div id='robinTimerWidget' class='robin-chat--sidebar-widget'>" + "<span style='font-size: 14px'>" + "<span id='reapTimerTime'>??</span>" + " until room is reaped" + "</span>" + "</div>"); } // Add configuration options to the sidebar addSetting("highlights","Highlight mentions",true); addSetting("stat-tracking","Report Tracking Statistics",true); addSetting("remove-votemotes","Remove vote emotes",true); addSetting("remove-botspam","Remove Old Bot Spam",true); addSetting("remove-duplicate-messages","Remove Duplicate Messages",true); addSetting("auto-quit-stay", "Auto-Quit Chat When Majority Stays", true); addSetting("auto-stay-big", "Stay When Room Size > 4000", true); addSetting("fast-clear", "/clear without animation", true); // monitor message sending listenForSubmit(); // With the statistics widget in place, populate it initially from local values updateStatistics(r.config); // Keep track of the room reap time updateReapTimer(); // 5 Seconds after we join, vote setTimeout(function(){ if(r.robin.stats.totalUsers > 4000 && GM_getValue("auto-stay-big",true)){ sendMessage("/vote stay"); }else{ sendMessage("/vote grow"); } }, 5 * 1000); // 60 Seconds after we load, trigger the statistics loop setTimeout(generateStatisticsQuery, 60 * 1000); // Create a hook for !commands var observer = new MutationObserver(newMessageHandler); $('#robinChatMessageList').each(function() { observer.observe(this,{childList: true}); }); })();