twitch spam filter

this script reduces twitch's chat spam

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name        twitch spam filter
// @description this script reduces twitch's chat spam
// @namespace   twitch
// @include     /^https?://www\.twitch\.tv/[a-zA-Z0-9_]+$/
// @version     0.1
// @grant       none
// ==/UserScript==

/* known issues :
** - the last chat message is sometimes hidden, you need to scroll a bit,
**   or wait for the next message to see it.
** - if you naviguate to another stream through the website,
**   the spam count will not show. You need to refresh the page to 
**   reset it.
*/

/* todo :
** - filter 'weird' text, which is most likely copypasta
*/

window.onload = function() {
    /* helper functions */
    var uniq = function(str) {
	var seen = {};
	return str.filter(function(item) {
            return seen.hasOwnProperty(item) ? false : (seen[item] = true);
	});
    };
    /* objects */
    /* Bucket */
    var Bucket = function(size) {
	this.maxSize = size;
	this.currentSize = 0;
	this.array = [];
    };
    Bucket.prototype.add = function(elem) {
	if (this.currentSize < this.maxSize) {
	    this.array.push(elem);
	    this.currentSize++;
	    return ;
	}
	this.array.shift();
	this.array.push(elem);
    };
    Bucket.prototype.contains = function(elem) {
	return this.array.indexOf(elem) != -1;
    };
    /* * Message */
    var Message = function(object) {
	this.object = object;
	this.parse(object);
    };
    Message.prototype.parse = function(object) {
	this.from = $(".from", object).text();
	this.content = $(".message", object).text().trim();
	this.emoticons = this.parseEmoticons($(".emoticon", object));
	this.badges = this.parseBadges($(".badge", object));
    };
    Message.prototype.parseEmoticons = function(object) {
	var parsed = [];
	object.each(function() {
	    parsed.push($(this).attr("alt"));
	});
	return parsed;
    };
    Message.prototype.parseBadges = function(object) {
	var parsed = [];
	object.each(function() {
	    parsed.push($(this).attr("class").split(" ").slice(3)[0]);
	});
	return parsed;
    };
    Message.prototype.show = function() {
	$(this.object).addClass("filtered");
	return $(this.object).show();
    };
    Message.prototype.remove = function() {
	return $(this.object).remove();
    };
    /* * MessageHandler */
    var MessageHandler = function() {
	this.filtered = 0;
	this.filteredBox = null;
	this.filterBoxActive = false
	this.whiteHandlers = []; // the message is shown if one hadnler returns true
	this.blackHandlers = []; // the message is filtered if one handler returns false
    };
    MessageHandler.prototype.init = function() {

    };
    MessageHandler.prototype.handleMessage = function(message) {
	for (var i = 0, max = this.whiteHandlers.length; i < max; i++) {
	    if (this.whiteHandlers[i](message) === true) {
		return true;
	    }
	}
	for (var i = 0, max = this.blackHandlers.length; i < max; i++) {
	    if (this.blackHandlers[i](message) === false) {
		return false;
	    }
	}
	return true;
    };
    MessageHandler.prototype.registerHandler = function(type, handler) {
	if (! this.hasOwnProperty(type)) {
	    return false;
	}
	this[type].push(handler);
	return true;
    };
    MessageHandler.prototype.createFilterBox = function() {
	this.filteredBox = $("<p class='filterbox' title='spam count' style='position:relative;top:-2px;left:8px;color: rgb(166, 142, 210)'></p>");
	if ($(".chat-buttons-container").append(this.filteredBox)) {
	    $(this.filteredBox).text(this.filtered);
	    this.filterBoxActive = true;
	}
    };
    MessageHandler.prototype.incFilterCount = function() {
	if (! this.filterBoxActive) {
	    this.createFilterBox();
	}
	this.filtered++;
	$(this.filteredBox).text(this.filtered);
    };
    /* whiteHandlers */
    var whitelist = [
	$(".username").text()
    ];
    var whitelistHandler = function(message) {
	for (var i = 0, max = whitelist.length; i < max; i++) {
	    if (message.from == whitelist[i]) {
		return true;
	    }
	}
	if (message.from == "") { // admin messages leave username blank
	    return true;
	}
	return false;
    };
    /* blackHandlers */
    var emoteHandler = function(message) {
	/* if the message is empty, it only contains emotes */
	if (! message.content.length) {
	    return false;
	}
	/* that's too much */
	if (message.emoticons.length >= 5) {
	    return false;
	}
	return true;
    };
    var numericHandler = function(message) {
	/* Kappa 123 */
	return isNaN(message.content);
    };
    var oneWordHandler = function(message) {
	/* who cares of false positives */
	return message.content.indexOf(" ") != -1;
    };
    var capsLockPleaseHandler = function(message) {
	var caps = 0;
	var alphaNum = 0;
	for (var i = 0, max = message.content.length; i < max; i++) {
	    if (message.content[i] >= 'A' && message.content[i] <= 'Z') {
		caps++;
		alphaNum++;
	    }
	    else if (message.content[i] != " ") {
		alphaNum++;
	    }
	}
	if (alphaNum / caps <= 2.0) {
	    return false;
	}
	return true;
    };
    var sameSameHandler = function(message) {
	var msg = message.content.split(" ");
	var uniqMsg = uniq(msg);
	if (msg.length / uniqMsg.length >= 1.7) {
	    return false;
	}
	return true;
    };
    var latestMessages = new Bucket(30);
    /* r9kHandler should be executed last, as storing a filtered
    ** message is useless */
    var r9kHandler = function(message) {
	var ret = true;
	if (latestMessages.contains(message.content)) {
	    ret = false;
	}
	latestMessages.add(message.content);
	return ret;
    };
    /* code */
    var hideMessages = function() {
	$("head").append("<style type='text/css'>.chat-line { display: none; }</style>");
    };
    var messageHandler = new MessageHandler();
    var whiteHandlers = [
	whitelistHandler
    ];
    var blackHandlers = [
	emoteHandler,
	numericHandler,
	oneWordHandler,
	capsLockPleaseHandler,
	sameSameHandler,
	r9kHandler
    ];
    messageHandler.init();
    for (var i = 0, max = whiteHandlers.length; i < max; i++) {
	messageHandler.registerHandler("whiteHandlers", whiteHandlers[i]);
    }
    for (var i = 0, max = blackHandlers.length; i < max; i++) {
	messageHandler.registerHandler("blackHandlers", blackHandlers[i]);
    }
    hideMessages();
    setInterval(function() {
	var messages = $(".chat-line").not(".filtered");
	messages.each(function() {
	    var message = new Message(this);
	    if (messageHandler.handleMessage(message)) {
		message.show();
	    }
	    else {
		message.remove();
		messageHandler.incFilterCount();
	    }
	});
    }, 200);
};