Guest User

Updated temporal twitch Pokemon script

a guest
Mar 2nd, 2014
1,621
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        Twitch Plays Pokemon Chat Filter
  3. // @namespace   https://github.com/jpgohlke/twitch-chat-filter
  4. // @description Hide input commands from the chat.
  5.  
  6. // @include     http://www.twitch.tv/twitchplayspokemon
  7. // @include     http://www.twitch.tv/twitchplayspokemon/
  8. // @include     http://www.twitch.tv/chat/embed?channel=twitchplayspokemon&popout_chat=true
  9.  
  10. // @version     1.6
  11. // @updateURL   http://jpgohlke.github.io/twitch-chat-filter/chat_filter.user.js
  12. // @grant       unsafeWindow
  13. // ==/UserScript==
  14.  
  15. /*
  16.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  17.  * of this software and associated documentation files (the "Software"), to deal
  18.  * in the Software without restriction, including without limitation the rights
  19.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  20.  * copies of the Software, and to permit persons to whom the Software is furnished
  21.  * to do so, subject to the following conditions:
  22.  *  
  23.  * The above copyright notice and this permission notice shall be included in all
  24.  * copies or substantial portions of the Software.
  25.  *  
  26.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  27.  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  28.  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  29.  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30.  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  31.  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32.  */
  33.  
  34. /*
  35.  * chat_filter.user.js
  36.  *
  37.  * Feel free to review/compress it yourself; good internet security is important!
  38.  * Passes http://www.jshint.com on default settings
  39.  * Contributors:
  40.  *     /u/RenaKunisaki
  41.  *     /u/smog_alado
  42.  *     /u/SRS-SRSLY
  43.  *     /u/schrobby
  44.  *     /u/red_agent
  45.  *     /u/DeathlyDeep
  46.  *     /u/jeff_gohlke
  47.  *     /u/yankjenets
  48.  *     /u/hugomg
  49.  *     /u/MKody
  50.  *     /u/feha
  51.  *     /u/jakery2
  52.  *     /u/redopium
  53.  *     /u/codefusion
  54.  */
  55.  
  56. /* global unsafeWindow:false */
  57. /* jshint lastsemic:true */
  58.  
  59.  
  60. (function(){
  61. "use strict";
  62.  
  63. // --- Script configuration ---
  64.  
  65. var TPP_COMMANDS = [
  66.     "left", "right", "up", "down",
  67.     "start", "select",
  68.     "a", "b",
  69.     "democracy", "anarchy", "wait"
  70. ];
  71.  
  72. // Score-based filter for "Guys, we need to beat Misty" spam.
  73. var MISTY_SUBSTRINGS = [
  74.     "misty",
  75.     "guys",
  76.     "we have to",
  77.     "we need to",
  78.     "beat",
  79. ];
  80.  
  81. var URL_WHITELIST = [
  82.     //us
  83.      "github.com",
  84.     //reddit
  85.     "reddit.com",
  86.     "webchat.freenode.net/?channels=twitchplayspokemon",
  87.     "sites.google.com/site/twitchplayspokemonstatus/",
  88.     "reddit.com/live/sw7bubeycai6hey4ciytwamw3a",
  89.     //miscelaneous
  90.     "strawpoll.me",
  91.     "imgur.com",
  92.     "pokeworld.herokuapp.com",
  93.     "strategywiki.org/wiki/Pok", //truncated before special characters
  94.     "vgmaps.com"
  95. ];
  96.  
  97. var BANNED_WORDS = [
  98.     "anus",
  99.     "giveaway", "t-shirt", "hoodie",
  100.     "imgur.com/4jlbxid.jpg"
  101. ];
  102.  
  103. var MINIMUM_DISTANCE_ERROR = 2; // Number of insertions / deletions / substitutions away from a blocked word.
  104. var MAXIMUM_NON_ASCII_CHARACTERS = 2; // For donger smilies, etc
  105. var MINIMUM_MESSAGE_WORDS = 2; // For Kappas and other short messages.
  106.  
  107. // The regexp Twitch uses to detect and automatically linkify URLs, with some modifications
  108. // so we can blacklist more messages.
  109. // - Recognizes *** in URLS (due to the Twitch chat censoring)
  110. // - Recognizes .mx and .sh TLDs
  111. var URL_REGEX = /\x02?((?:https?:\/\/|[\w\-\.\+]+@)?\x02?(?:[\w\-\*]+\x02?\.)+\x02?(?:com|au|org|tv|net|info|jp|uk|us|cn|fr|mobi|gov|co|ly|me|vg|eu|ca|fm|am|ws|mx|sh)\x02?(?:\:\d+)?\x02?(?:\/[\w\.\/@\?\&\%\#\(\)\,\-\+\=\;\:\x02?]+\x02?[\w\/@\?\&\%\#\(\)\=\;\x02?]|\x02?\w\x02?|\x02?)?\x02?)\x02?/g;
  112. var CENSORED_URL = /\*\*\*[\/\?\#\%]/g;
  113.  
  114. // --- Greasemonkey loading ---
  115.  
  116. //Greasemonkey userscripts run in a separate environment and cannot use
  117. //global variables from the page directly. We needd to access them via unsafeWindow
  118. var myWindow;
  119. try{
  120.     myWindow = unsafeWindow;
  121. }catch(e){
  122.     myWindow = window;
  123. }
  124.  
  125. var $ = myWindow.jQuery;
  126. var CurrentChat = null;
  127. var chat_loaded = false;
  128.    
  129. // --- Filtering predicates ---
  130.  
  131. // Adapted from https://gist.github.com/andrei-m/982927
  132. // Compute the edit distance between the two given strings
  133. function min_edit(a, b) {
  134.    
  135.     if(a.length === 0) return b.length;
  136.     if(b.length === 0) return a.length;
  137.  
  138.     var matrix = [];
  139.  
  140.     // increment along the first column of each row
  141.     for(var i = 0; i <= b.length; i++) {
  142.         matrix[i] = [i];
  143.     }
  144.  
  145.     // increment each column in the first row
  146.     for(var j = 0; j <= a.length; j++) {
  147.         matrix[0][j] = j;
  148.     }
  149.  
  150.     // Fill in the rest of the matrix
  151.     for(var i = 1; i <= b.length; i++) {
  152.         for(var j = 1; j <= a.length; j++) {
  153.             if(b.charAt(i-1) == a.charAt(j-1)){
  154.                 matrix[i][j] = matrix[i-1][j-1];
  155.             } else {
  156.                 matrix[i][j] = 1 + Math.min(
  157.                     matrix[i-1][j-1], // substitution
  158.                     matrix[i][j-1]  , // insertion
  159.                     matrix[i-1][j]    // deletion
  160.                 );
  161.             }
  162.         }
  163.     }
  164.  
  165.     return matrix[b.length][a.length];
  166. }
  167.  
  168. //This regex recognizes messages that contain exactly a chat command,
  169. //without any extra words around. This includes compound democracy mode
  170. //commands like `up2left4` and `start9`.
  171. // (remember to escape the backslashes when building a regexes from strings!)
  172. var compound_command_regex = new RegExp("^((" + TPP_COMMANDS.join("|") + ")\\d*)+$", "i");
  173.  
  174. function word_is_command(word){
  175.  
  176.     if(compound_command_regex.test(word)) return true;
  177.  
  178.     for(var j=0; j<TPP_COMMANDS.length; j++){
  179.         var cmd = TPP_COMMANDS[j];
  180.          
  181.         if(min_edit(cmd, word) <= MINIMUM_DISTANCE_ERROR){
  182.            return true;
  183.         }
  184.     }
  185.     return false;  
  186. }
  187.  
  188. function message_is_command(message){
  189.     message = message.toLowerCase();
  190.    
  191.     var segments = message.split(/[\d\s]+/);
  192.    
  193.     for(var i=0; i<segments.length; i++){
  194.         var segment = segments[i];
  195.         if(!segment) continue;
  196.         if(!word_is_command(segment)) return false;
  197.     }
  198.    
  199.     return true;
  200. }
  201.  
  202.  
  203. function message_is_spam(message) {
  204.     message = message.toLowerCase();
  205.    
  206.     for(var i=0; i < BANNED_WORDS.length; i++){
  207.         if(0 <= message.indexOf(BANNED_WORDS[i])){
  208.             return true;
  209.         }
  210.     }
  211.    
  212.     // Determine if message is variant of "Guys, we need to beat Misty."
  213.     var misty_score = 0;
  214.     for (var i = 0; i < MISTY_SUBSTRINGS.length; i++) {
  215.         if (message.indexOf(MISTY_SUBSTRINGS[i]) != -1) {
  216.             misty_score++;
  217.             if (misty_score > 1) {    
  218.                 return true;
  219.             }
  220.         }
  221.     }
  222.    
  223.     return false;
  224. }
  225.  
  226. function is_whitelisted_url(url){
  227.     //This doesnt actually parse the URLs but it
  228.     //should do the job when it comes to filtering.
  229.     for(var i=0; i<URL_WHITELIST.length; i++){
  230.         if(0 <= url.indexOf(URL_WHITELIST[i])){
  231.             return true;
  232.         }
  233.     }
  234.     return false;
  235. }
  236.  
  237. function message_is_forbidden_link(message){
  238.     message = message.toLowerCase();
  239.  
  240.     if(CENSORED_URL.test(message)) return true;
  241.  
  242.     var urls = message.match(URL_REGEX);
  243.     if(!urls) return false;
  244.    
  245.     for(var i=0; i<urls.length; i++){
  246.         if(!is_whitelisted_url(urls[i])){
  247.             return true;
  248.         }
  249.     }
  250.    
  251.     return false;
  252. }
  253.  
  254. function message_is_donger(message){
  255.     var nonASCII = 0;
  256.     for(var i = 0; i < message.length; i++) {
  257.         if(message.charCodeAt(i) > 127) {
  258.             nonASCII++;
  259.             if(nonASCII > MAXIMUM_NON_ASCII_CHARACTERS){
  260.                 return true;
  261.             }
  262.         }
  263.     }
  264.     return false;
  265. }
  266.  
  267. function message_is_small(message){
  268.     return message.split(/\s/g).length < MINIMUM_MESSAGE_WORDS;
  269. }
  270.  
  271. function message_is_cyrillic(message){
  272.     //Some people use cyrillic characters to write spam that gets past the filter.
  273.     return /[\u0400-\u04FF]/.test(message);
  274. }
  275.  
  276. function convert_copy_paste(message){
  277.     //Replace repetitive text with only one instance of it
  278.     //Useful for text and links where people do
  279.     // ctrl-c ctrl-v ctrl-v ctrl-v in order to increase the
  280.     //size of the message.
  281.     return message.replace(/(.{4}.*?)(\s*?\1)+/g, "$1");
  282. }
  283.  
  284. // --- Filtering ---
  285.  
  286. //Filters have predicates that are called for every message
  287. //to determine whether it should get dropped or not
  288. var filters = [
  289.   { name: 'TppFilterCommand',
  290.     comment: "Commands (up, down, anarchy, etc)",
  291.     isActive: true,
  292.     predicate: message_is_command
  293.   },
  294.  
  295.   { name: 'TppFilterLink',
  296.     comment: "Non-whitelisted URLs",
  297.     isActive: true,
  298.     predicate: message_is_forbidden_link
  299.   },
  300.  
  301.   { name: 'TppFilterDonger',
  302.     comment: "Ascii art and dongers",
  303.     isActive: false,
  304.     predicate: message_is_donger
  305.   },
  306.  
  307.   { name: 'TppFilterSmall',
  308.     comment: "One-word messages",
  309.     isActive: false,
  310.     predicate: message_is_small
  311.   },
  312.  
  313.   { name: 'TppFilterSpam',
  314.     comment: 'Spam',
  315.     isActive: true,
  316.     predicate: message_is_spam
  317.   },
  318.  
  319.   { name: 'TppFilterCyrillic',
  320.     comment: 'Cyrillic characters',
  321.     isActive: true,
  322.     predicate: message_is_cyrillic
  323.   }
  324. ];
  325.  
  326.  
  327. //Rewriters are applied to the text of a message
  328. //before it is inserted in the chat box
  329. var rewriters = [  
  330.   { name: 'TppFilterDuplicateURL',
  331.     comment: "Copy pasted repetitions",
  332.     isActive: true,
  333.     rewriter: convert_copy_paste
  334.   },
  335. ];
  336.  
  337. //Stylers are CSS classes that get toggled on/off
  338. var stylers = [
  339.   { name: 'TppConvertAllcaps',
  340.     comment: "Lowercase-only mode",
  341.     isActive: true,
  342.     element: '.chat-messages',
  343.     class: 'allcaps_filtered'
  344.   },
  345. ];
  346.  
  347. function passes_active_filters(message){
  348.     for(var i=0; i < filters.length; i++){
  349.         var filter = filters[i];
  350.         if(filter.isActive && filter.predicate(message)){
  351.             //console.log("Filter", filter.name, message);
  352.             return false;
  353.         }
  354.     }
  355.     return true;
  356. }
  357.  
  358. function rewrite_with_active_rewriters(message){
  359.     var newMessage = message;
  360.     for(var i=0;  i < rewriters.length; i++){
  361.         var rewriter = rewriters[i];
  362.         if(rewriter.isActive){
  363.             newMessage = (rewriter.rewriter(newMessage) || newMessage);
  364.         }
  365.     }
  366.     return newMessage;
  367. }
  368.  
  369. // --- UI ---
  370.  
  371. function initialize_ui(){
  372.  
  373.     //TODO: #chat_line_list li.fromjtv
  374.  
  375.     $("button.viewers").after('<a id="chat_filter_dropmenu_button" class="dropdown_glyph"><span></span><a>');
  376.     $('#chat_filter_dropmenu_button').on('click', function(){
  377.         $('#chat_filter_dropmenu').toggle();
  378.     });
  379.    
  380.     $('#chat_filter_dropmenu_button span')
  381.         .css('background', 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAv0lEQVQ4jc3SIQ7CQBAF0C8rK5E9AhI5R1gccpLOn+UACARHwCO5Aq6HQHAUQsAhwJGmlNBdIOEnY18mfwb4u4hIYWaSOySnAABVrWKMt9xx97OqVlDVkbufPoAuZiYAgBBC6e5NBnJQ1eqpK5KbBKQJIZQvyyc5f4eQ3A66pJlJjLG3N3dfJr0FyUUHudZ1PUtCWls9IDPbJyN90OBeulHV8beg6lfQKgsSkaJ18qOZTbIgAHD3NcmdiBTZSGruBIYOSjStwb0AAAAASUVORK5CYII=)')
  382.         .css('position', 'relative');
  383.        
  384.     $('.send-chat-button').css('left', '107px');
  385.     $('.chat-interface').append('<div id="chat_filter_dropmenu" class="dropmenu menu-like" style="position:absolute; bottom:45px; display:none;"><p style="margin-left:6px">Hide:</p></div>');
  386.    
  387.    
  388.     var controlPanel = $('#chat_filter_dropmenu');
  389.    
  390.     var customCssParts = [
  391.         ".chat-messages .TppFiltered {display:none;} .filter_option{font-weight:normal; margin-bottom:0; color: #B9A3E3;}",
  392.         ".chat-messages.allcaps_filtered span.chat-line{text-transform:lowercase;}"
  393.     ];
  394.  
  395.     $('head').append('<style>' + customCssParts.join("") + '</style>');
  396.    
  397.     function add_option(option, update){
  398.         controlPanel
  399.         .append('<p class="dropmenu_action"><label for="' + option.name + '" class="filter_option"> <input type="checkbox" id="' + option.name + '">' + option.comment + '</label></p>');
  400.  
  401.         $('#' + option.name)
  402.         .on('change', function(){
  403.             option.isActive = $(this).prop("checked");
  404.             update(option);
  405.                
  406.         })
  407.         .prop('checked', option.isActive);
  408.     }
  409.    
  410.     filters.forEach(function(filter){
  411.         add_option(filter, update_chat_with_filter);
  412.     });
  413.     $('#chat_filter_dropmenu').append('<p style="margin-left:6px;">Automatically rewrite:</p>');
  414.     rewriters.forEach(function(rewriter){
  415.         add_option(rewriter, function(rewriter){});
  416.     });
  417.     function update_css(styler){
  418.         if(styler.isActive)
  419.             $(styler.element).addClass(styler.class);
  420.         else
  421.             $(styler.element).removeClass(styler.class);
  422.     }
  423.     stylers.forEach(function(option){
  424.         add_option(option, update_css);
  425.         update_css(option);
  426.     });
  427. }
  428.  
  429.  
  430. // --- Main ---
  431.  
  432. function update_chat_with_filter(){
  433.     $('.chat-line').each(function() {
  434.         var chatLine = $(this);
  435.         var chatText = chatLine.find(".message").text().trim();
  436.        
  437.         if(passes_active_filters(chatText)){
  438.             chatLine.removeClass("TppFiltered");
  439.         }else{
  440.             chatLine.addClass("TppFiltered");
  441.         }
  442.     });
  443. }
  444.  
  445. $(function(){
  446.     //Checking for the spinner being gone is a more reliable way to chack
  447.     //if the CurrentChat is fully loaded.
  448.     var chatLoadedCheck = setInterval(function () {
  449.         if($(".loading-mask").length == 0){
  450.             chat_loaded = true;
  451.             clearInterval(chatLoadedCheck);
  452.             initialize_ui();
  453.             update_chat_with_filter();
  454.         }
  455.     }, 100);
  456.    
  457.    
  458.  
  459.     window.setInterval(function(){
  460.         if(chat_loaded)
  461.         {
  462.             update_chat_with_filter();
  463.         }
  464.     }, 10);
  465.  
  466.  
  467.    
  468. });
  469.    
  470. }());
Add Comment
Please, Sign In to add comment