diff --git a/lib/channel.js b/lib/channel.js index c5f09fe7..792b0e30 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -84,14 +84,19 @@ var Channel = function(name) { self.opts = { allow_voteskip: true, voteskip_ratio: 0.5, - afk_timeout: 180, + afk_timeout: 600, pagetitle: self.name, maxlength: 0, externalcss: "", externaljs: "", chat_antiflood: false, + chat_antiflood_params: { + burst: 4, + sustained: 1, + cooldown: 4 + }, show_public: false, - enable_link_regex: true + enable_link_regex: true, }; self.filters = [ new Filter("monospace", "`([^`]+)`", "g", "$1"), @@ -2099,12 +2104,27 @@ Channel.prototype.tryUpdateOptions = function(user, data) { if(key in adminonly && user.rank < 3) { continue; } + if (key === "chat_antiflood_params") { + var b = parseInt(data[key].burst); + if (isNaN(b) || b < 0) + b = 1; + var s = parseFloat(data[key].sustained); + if (isNaN(s) || s <= 0) + s = 1; + var c = parseFloat(data[key].cooldown); + if (isNaN(c) || c < 0) + c = 0; + this.opts.chat_antiflood_params.burst = b; + this.opts.chat_antiflood_params.sustained = s; + this.opts.chat_antiflood_params.cooldown = c; + continue; + } + this.opts[key] = data[key]; if(key === "afk_timeout" && this.opts[key] != data[key]) { this.users.forEach(function (u) { u.autoAFK(); }); } - this.opts[key] = data[key]; } } @@ -2222,7 +2242,16 @@ Channel.prototype.tryChat = function(user, data) { if(msg.length > 240) { msg = msg.substring(0, 240); } - if(this.opts.chat_antiflood && user.noflood("chat", 2.0)) { + + if (this.opts.chat_antiflood && + user.chatLimiter.throttle(this.opts.chat_antiflood_params)) { + user.socket.emit("chatCooldown", 1000/this.opts.chat_antiflood_params.sustained); + /* + user.socket.emit("noflood", { + action: "chat", + msg: "sending messages too quickly!" + }); + */ return; } diff --git a/lib/user.js b/lib/user.js index 07b56f43..3dfe11bd 100644 --- a/lib/user.js +++ b/lib/user.js @@ -36,6 +36,7 @@ var User = function (socket) { this.throttle = {}; this.flooded = {}; this.queueLimiter = $util.newRateLimiter(); + this.chatLimiter = $util.newRateLimiter(); this.profile = { image: "", text: "" diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index c0f61ed0..412448ee 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -127,6 +127,43 @@ Callbacks = { scrollChat(); }, + chatCooldown: function (time) { + time = time + 200; + /* + var msg = $("#chat-cooldown-msg"); + if (msg.length > 0) { + var timer = msg.data("timer"); + if (timer) { + clearTimeout(timer); + } + } else { + msg = $("
") + .addClass("server-msg-disconnect") + .attr("id", "chat-cooldown-msg") + .text("Chat rate limit exceeded, please wait before sending another message") + .appendTo($("#messagebuffer")); + } + if (SCROLLCHAT) { + scrollChat(); + } + */ + $("#chatline").css("color", "#ff0000"); + if (CHATTHROTTLE && $("#chatline").data("throttle_timer")) { + clearTimeout($("#chatline").data("throttle_timer")); + } + CHATTHROTTLE = true; + $("#chatline").data("throttle_timer", setTimeout(function () { + CHATTHROTTLE = false; + $("#chatline").css("color", ""); + /* + msg.remove(); + if (SCROLLCHAT) { + scrollChat(); + } + */ + }, time)); + }, + channelNotRegistered: function() { var div = $("
").addClass("alert alert-info") .attr("id", "chregnotice") diff --git a/www/assets/js/channelsettings.js b/www/assets/js/channelsettings.js index 23a2fcbc..0492b29b 100644 --- a/www/assets/js/channelsettings.js +++ b/www/assets/js/channelsettings.js @@ -67,6 +67,10 @@ else { len = parseInt(hms[0]); } + var sus = parseFloat($("#opt_chat_antiflood_sustained").val()) || 0; + if (sus <= 0) { + sus = 1; + } socket.emit("setOptions", { allow_voteskip: $("#opt_allow_voteskip").prop("checked"), voteskip_ratio: parseFloat($("#opt_voteskip_ratio").val()), @@ -75,6 +79,11 @@ externalcss: $("#opt_externalcss").val(), externaljs: $("#opt_externaljs").val(), chat_antiflood: $("#opt_chat_antiflood").prop("checked"), + chat_antiflood_params: { + burst: $("#opt_chat_antiflood_burst").val(), + sustained: $("#opt_chat_antiflood_sustained").val(), + cooldown: $("#opt_chat_antiflood_cooldown").val() + }, show_public: $("#opt_show_public").prop("checked"), enable_link_regex: $("#opt_enable_link_regex").prop("checked"), afk_timeout: parseInt($("#opt_afktimeout").val()) diff --git a/www/assets/js/data.js b/www/assets/js/data.js index be892386..33db153d 100644 --- a/www/assets/js/data.js +++ b/www/assets/js/data.js @@ -54,6 +54,7 @@ var socket = { var IGNORED = []; var CHATHIST = []; var CHATHISTIDX = 0; +var CHATTHROTTLE = false; var SCROLLCHAT = true; var LASTCHATNAME = ""; var LASTCHATTIME = 0; diff --git a/www/assets/js/ui.js b/www/assets/js/ui.js index cbff5142..7abed817 100644 --- a/www/assets/js/ui.js +++ b/www/assets/js/ui.js @@ -148,6 +148,9 @@ $("#messagebuffer").mouseleave(function() { SCROLLCHAT = true; }); $("#chatline").keydown(function(ev) { if(ev.keyCode == 13) { + if (CHATTHROTTLE) { + return; + } var msg = $("#chatline").val(); if(msg.trim()) { var meta = {}; diff --git a/www/assets/js/util.js b/www/assets/js/util.js index 774a7c27..0fc13436 100644 --- a/www/assets/js/util.js +++ b/www/assets/js/util.js @@ -1010,6 +1010,9 @@ function handleModPermissions() { $("#opt_externaljs").val(CHANNEL.opts.externaljs); $("#opt_externaljs").attr("disabled", CLIENT.rank < 3); $("#opt_chat_antiflood").prop("checked", CHANNEL.opts.chat_antiflood); + $("#opt_chat_antiflood_burst").val(CHANNEL.opts.chat_antiflood_params.burst); + $("#opt_chat_antiflood_sustained").val(CHANNEL.opts.chat_antiflood_params.sustained); + $("#opt_chat_antiflood_cooldown").val(CHANNEL.opts.chat_antiflood_params.cooldown); $("#opt_show_public").prop("checked", CHANNEL.opts.show_public); $("#opt_show_public").attr("disabled", CLIENT.rank < 3); $("#opt_enable_link_regex").prop("checked", CHANNEL.opts.enable_link_regex); diff --git a/www/channeloptions.html b/www/channeloptions.html index 699dc9fe..41d984da 100644 --- a/www/channeloptions.html +++ b/www/channeloptions.html @@ -29,6 +29,27 @@
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+