From 027836b78f8647a29ed096097ab8aa4963190cc4 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 1 Oct 2012 17:33:05 +0200 Subject: [PATCH] Thread creation cooldown. Fix cooldown between different post-type. Fix /q/ cooldowns? Ugh. see #774 --- 4chan_x.user.js | 155 +++++++++++++++++++++++++++++++++++------------- changelog | 4 ++ script.coffee | 149 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 225 insertions(+), 83 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 966222bde..b9d5e6b9a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1738,7 +1738,7 @@ $.on(link.firstChild, 'click', function() { QR.open(); if (!g.REPLY) { - $('select', QR.el).value = 'new'; + QR.threadSelector.value = 'new'; } return $('textarea', QR.el).focus(); }); @@ -1836,44 +1836,118 @@ }, cooldown: { init: function() { - var length, timeout, _ref; if (!Conf['Cooldown']) { return; } - _ref = $.get("/" + g.BOARD + "/cooldown", {}), timeout = _ref.timeout, length = _ref.length; - if (timeout) { - QR.cooldown.start(timeout, length); + QR.cooldown.types = { + thread: (function() { + switch (g.BOARD) { + case 'q': + return 86400; + case 'b': + case 'soc': + case 'r9k': + return 600; + default: + return 300; + } + })(), + sage: g.BOARD === 'q' ? 600 : 60, + file: g.BOARD === 'q' ? 300 : 30, + post: g.BOARD === 'q' ? 60 : 30 + }; + QR.cooldown.cooldowns = $.get("" + g.BOARD + ".cooldown", {}); + QR.cooldown.start(); + return $.sync("" + g.BOARD + ".cooldown", QR.cooldown.sync); + }, + start: function() { + if (QR.cooldown.isCounting) { + return; } - return $.sync("/" + g.BOARD + "/cooldown", QR.cooldown.start); + QR.cooldown.isCounting = true; + return QR.cooldown.count(); }, - start: function(timeout, length) { - var seconds; - seconds = Math.floor((timeout - Date.now()) / 1000); - return QR.cooldown.count(seconds, length); + sync: function(cooldowns) { + QR.cooldown.cooldowns = cooldowns; + return QR.cooldown.start(); }, - set: function(seconds) { + set: function(data) { + var cooldown, hasFile, isReply, isSage, start, type; if (!Conf['Cooldown']) { return; } - QR.cooldown.count(seconds, seconds); - return $.set("/" + g.BOARD + "/cooldown", { - timeout: Date.now() + seconds * $.SECOND, - length: seconds - }); + start = Date.now(); + if (data.delay) { + cooldown = { + delay: data.delay + }; + } else { + isSage = /sage/i.test(data.post.email); + hasFile = !!data.post.file; + isReply = data.isReply; + type = !isReply ? 'thread' : isSage ? 'sage' : hasFile ? 'file' : 'post'; + cooldown = { + isReply: isReply, + isSage: isSage, + hasFile: hasFile, + timeout: start + QR.cooldown.types[type] * $.SECOND + }; + } + QR.cooldown.cooldowns[start] = cooldown; + $.set("" + g.BOARD + ".cooldown", QR.cooldown.cooldowns); + return QR.cooldown.start(); }, - count: function(seconds, length) { - if (!((0 <= seconds && seconds <= length))) { + unset: function(id) { + delete QR.cooldown.cooldowns[id]; + return $.set("" + g.BOARD + ".cooldown", QR.cooldown.cooldowns); + }, + count: function() { + var cooldown, cooldowns, elapsed, hasFile, isReply, isSage, now, post, seconds, start, type, types, _ref; + if (Object.keys(QR.cooldown.cooldowns).length) { + setTimeout(QR.cooldown.count, 1000); + } else { + $["delete"]("" + g.BOARD + ".cooldown"); + QR.cooldown.isCounting = false; return; } - setTimeout(QR.cooldown.count, 1000, seconds - 1, length); - QR.cooldown.seconds = seconds; - if (seconds === 0) { - $["delete"]("/" + g.BOARD + "/cooldown"); - if (QR.cooldown.auto) { - QR.submit(); + if ((isReply = g.REPLY ? true : QR.threadSelector.value !== 'new')) { + post = QR.replies[0]; + isSage = /sage/i.test(post.email); + hasFile = !!post.file; + } + now = Date.now(); + seconds = null; + _ref = QR.cooldown, types = _ref.types, cooldowns = _ref.cooldowns; + for (start in cooldowns) { + cooldown = cooldowns[start]; + if ('delay' in cooldown) { + if (cooldown.delay) { + seconds = Math.max(seconds, cooldown.delay--); + } else { + seconds = Math.max(seconds, 0); + QR.cooldown.unset(start); + } + continue; + } + type = isReply && cooldown.isReply ? isSage && cooldown.isSage ? 'sage' : hasFile && cooldown.hasFile ? 'file' : 'post' : !(isReply || cooldown.isReply) ? type = 'thread' : void 0; + if (type) { + elapsed = Math.floor((now - start) / 1000); + if (elapsed >= 0) { + seconds = Math.max(seconds, types[type] - elapsed); + } + type = ''; + } + if (!((start <= now && now <= cooldown.timeout))) { + QR.cooldown.unset(start); } } - return QR.status(); + QR.cooldown.seconds = seconds; + if (seconds !== null) { + QR.status(); + } + if (seconds === 0 && QR.cooldown.auto) { + return QR.submit(); + } } }, quote: function(e) { @@ -1883,7 +1957,7 @@ } QR.open(); if (!g.REPLY) { - $('select', QR.el).value = $.x('ancestor::div[parent::div[@class="board"]]', this).id.slice(1); + QR.threadSelector.value = $.x('ancestor::div[parent::div[@class="board"]]', this).id.slice(1); } id = this.previousSibling.hash.slice(2); text = ">>" + id + "\n"; @@ -2173,10 +2247,7 @@ (QR.replies[index - 1] || QR.replies[index + 1]).select(); } QR.replies.splice(index, 1); - if (typeof (_base1 = window.URL || window.webkitURL).revokeObjectURL === "function") { - _base1.revokeObjectURL(this.url); - } - return delete this; + return typeof (_base1 = window.URL || window.webkitURL).revokeObjectURL === "function" ? _base1.revokeObjectURL(this.url) : void 0; }; return _Class; @@ -2342,11 +2413,12 @@ id = thread.id.slice(1); threads += ""; } - $.prepend($('.move > span', QR.el), $.el('select', { + QR.threadSelector = $.el('select', { innerHTML: threads, title: 'Create a new thread / Reply to a thread' - })); - $.on($('select', QR.el), 'mousedown', function(e) { + }); + $.prepend($('.move > span', QR.el), QR.threadSelector); + $.on(QR.threadSelector, 'mousedown', function(e) { return e.stopPropagation(); }); } @@ -2380,7 +2452,7 @@ $.on($("[name=" + name + "]", QR.el), 'input', function() { var _ref2; QR.selected[this.name] = this.value; - if (QR.cooldown.auto && QR.selected === QR.replies[0] && (0 < (_ref2 = QR.cooldown.seconds) && _ref2 < 6)) { + if (QR.cooldown.auto && QR.selected === QR.replies[0] && (0 < (_ref2 = QR.cooldown.seconds) && _ref2 <= 5)) { return QR.cooldown.auto = false; } }); @@ -2406,7 +2478,7 @@ } QR.abort(); reply = QR.replies[0]; - threadID = g.THREAD_ID || $('select', QR.el).value; + threadID = g.THREAD_ID || QR.threadSelector.value; if (threadID === 'new') { if (((_ref = g.BOARD) === 'vg' || _ref === 'q') && !reply.sub) { err = 'New threads require a subject.'; @@ -2506,7 +2578,7 @@ return QR.ajax = $.ajax($.id('postForm').parentNode.action, callbacks, opts); }, response: function(html) { - var bs, doc, err, msg, persona, postID, reply, sage, seconds, threadID, _, _ref, _ref1; + var bs, doc, err, msg, persona, postID, reply, threadID, _, _ref, _ref1; doc = d.implementation.createHTMLDocument(''); doc.documentElement.innerHTML = html; if (doc.title === '4chan - Banned') { @@ -2527,7 +2599,9 @@ err.textContent = 'Error: You seem to have mistyped the CAPTCHA.'; } QR.cooldown.auto = QR.captchaIsEnabled ? !!$.get('captchas', []).length : true; - QR.cooldown.set(2); + QR.cooldown.set({ + delay: 2 + }); } else { QR.cooldown.auto = false; } @@ -2551,13 +2625,14 @@ postID: postID } })); + QR.cooldown.set({ + post: reply, + isReply: threadID !== '0' + }); if (threadID === '0') { location.pathname = "/" + g.BOARD + "/res/" + postID; } else { QR.cooldown.auto = QR.replies.length > 1; - sage = /sage/i.test(reply.email); - seconds = g.BOARD === 'q' ? sage ? 600 : reply.file ? 300 : 60 : sage ? 60 : 30; - QR.cooldown.set(seconds); if (Conf['Open Reply in New Tab'] && !g.REPLY && !QR.cooldown.auto) { $.open("//boards.4chan.org/" + g.BOARD + "/res/" + threadID + "#p" + postID); } diff --git a/changelog b/changelog index 3b568d710..971bddfa3 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,8 @@ master +- Mayhem + Added thread creation QR cooldown. + Fix QR cooldown timer between non-sage and sage posts. You can submit a non-sage post 30 seconds after a sage one. + Fix /q/ QR cooldowns. 2.35.4 - Mayhem diff --git a/script.coffee b/script.coffee index 67bc142f0..d5c15bc64 100644 --- a/script.coffee +++ b/script.coffee @@ -1342,7 +1342,7 @@ QR = link = $.el 'h1', innerHTML: "#{if g.REPLY then 'Reply to Thread' else 'Start a Thread'}" $.on link.firstChild, 'click', -> QR.open() - $('select', QR.el).value = 'new' unless g.REPLY + QR.threadSelector.value = 'new' unless g.REPLY $('textarea', QR.el).focus() $.before $.id('postForm'), link @@ -1418,32 +1418,108 @@ QR = cooldown: init: -> return unless Conf['Cooldown'] - {timeout, length} = $.get "/#{g.BOARD}/cooldown", {} - QR.cooldown.start timeout, length if timeout - $.sync "/#{g.BOARD}/cooldown", QR.cooldown.start - start: (timeout, length) -> - seconds = Math.floor (timeout - Date.now()) / 1000 - QR.cooldown.count seconds, length - set: (seconds) -> + QR.cooldown.types = + thread: switch g.BOARD + when 'q' then 86400 + when 'b', 'soc', 'r9k' then 600 + else 300 + sage: if g.BOARD is 'q' then 600 else 60 + file: if g.BOARD is 'q' then 300 else 30 + post: if g.BOARD is 'q' then 60 else 30 + QR.cooldown.cooldowns = $.get "#{g.BOARD}.cooldown", {} + QR.cooldown.start() + $.sync "#{g.BOARD}.cooldown", QR.cooldown.sync + start: -> + return if QR.cooldown.isCounting + QR.cooldown.isCounting = true + QR.cooldown.count() + sync: (cooldowns) -> + QR.cooldown.cooldowns = cooldowns + QR.cooldown.start() + set: (data) -> return unless Conf['Cooldown'] - QR.cooldown.count seconds, seconds - $.set "/#{g.BOARD}/cooldown", - timeout: Date.now() + seconds * $.SECOND - length: seconds - count: (seconds, length) -> - return unless 0 <= seconds <= length - setTimeout QR.cooldown.count, 1000, seconds-1, length + start = Date.now() + if data.delay + cooldown = delay: data.delay + else + isSage = /sage/i.test data.post.email + hasFile = !!data.post.file + isReply = data.isReply + type = + unless isReply + 'thread' + else + if isSage + 'sage' + else if hasFile + 'file' + else + 'post' + cooldown = + isReply: isReply + isSage: isSage + hasFile: hasFile + timeout: start + QR.cooldown.types[type] * $.SECOND + QR.cooldown.cooldowns[start] = cooldown + $.set "#{g.BOARD}.cooldown", QR.cooldown.cooldowns + QR.cooldown.start() + unset: (id) -> + delete QR.cooldown.cooldowns[id] + $.set "#{g.BOARD}.cooldown", QR.cooldown.cooldowns + count: -> + if Object.keys(QR.cooldown.cooldowns).length + setTimeout QR.cooldown.count, 1000 + else + $.delete "#{g.BOARD}.cooldown" + QR.cooldown.isCounting = false + return + + if (isReply = if g.REPLY then true else QR.threadSelector.value isnt 'new') + post = QR.replies[0] + isSage = /sage/i.test post.email + hasFile = !!post.file + now = Date.now() + seconds = null + {types, cooldowns} = QR.cooldown + + for start, cooldown of cooldowns + if 'delay' of cooldown + if cooldown.delay + seconds = Math.max seconds, cooldown.delay-- + else + seconds = Math.max seconds, 0 + QR.cooldown.unset start + continue + + # Only cooldowns relevant to this post can set the seconds value. + # Unset outdated cooldowns that can no longer impact us. + type = + if isReply and cooldown.isReply + if isSage and cooldown.isSage + 'sage' + else if hasFile and cooldown.hasFile + 'file' + else + 'post' + else unless isReply or cooldown.isReply + type = 'thread' + if type + elapsed = Math.floor (now - start) / 1000 + if elapsed >= 0 # clock changed since then? + seconds = Math.max seconds, types[type] - elapsed + type = '' + unless start <= now <= cooldown.timeout + QR.cooldown.unset start + QR.cooldown.seconds = seconds - if seconds is 0 - $.delete "/#{g.BOARD}/cooldown" - QR.submit() if QR.cooldown.auto - QR.status() + QR.status() if seconds isnt null + QR.submit() if seconds is 0 and QR.cooldown.auto quote: (e) -> e?.preventDefault() QR.open() unless g.REPLY - $('select', QR.el).value = $.x('ancestor::div[parent::div[@class="board"]]', @).id[1..] + QR.threadSelector.value = $.x('ancestor::div[parent::div[@class="board"]]', @).id[1..] # Make sure we get the correct number, even with XXX censors id = @previousSibling.hash[2..] text = ">>#{id}\n" @@ -1674,7 +1750,6 @@ QR = (QR.replies[index-1] or QR.replies[index+1]).select() QR.replies.splice index, 1 (window.URL or window.webkitURL).revokeObjectURL? @url - delete @ captcha: init: -> @@ -1804,10 +1879,11 @@ QR = for thread in $$ '.thread' id = thread.id[1..] threads += "" - $.prepend $('.move > span', QR.el), $.el 'select' + QR.threadSelector = $.el 'select' innerHTML: threads title: 'Create a new thread / Reply to a thread' - $.on $('select', QR.el), 'mousedown', (e) -> e.stopPropagation() + $.prepend $('.move > span', QR.el), QR.threadSelector + $.on QR.threadSelector, 'mousedown', (e) -> e.stopPropagation() $.on $('#autohide', QR.el), 'change', QR.toggleHide $.on $('.close', QR.el), 'click', QR.close $.on $('#dump', QR.el), 'click', -> QR.el.classList.toggle 'dump' @@ -1828,7 +1904,7 @@ QR = QR.selected[@name] = @value # Disable auto-posting if you're typing in the first reply # during the last 5 seconds of the cooldown. - if QR.cooldown.auto and QR.selected is QR.replies[0] and 0 < QR.cooldown.seconds < 6 + if QR.cooldown.auto and QR.selected is QR.replies[0] and 0 < QR.cooldown.seconds <= 5 QR.cooldown.auto = false QR.status.input = $ 'input[type=submit]', QR.el @@ -1851,7 +1927,7 @@ QR = QR.abort() reply = QR.replies[0] - threadID = g.THREAD_ID or $('select', QR.el).value + threadID = g.THREAD_ID or QR.threadSelector.value # prevent errors if threadID is 'new' @@ -1972,7 +2048,7 @@ QR = true # Too many frequent mistyped captchas will auto-ban you! # On connection error, the post most likely didn't go through. - QR.cooldown.set 2 + QR.cooldown.set delay: 2 else # stop auto-posting QR.cooldown.auto = false QR.status() @@ -1997,29 +2073,16 @@ QR = threadID: threadID postID: postID + QR.cooldown.set + post: reply + isReply: threadID isnt '0' + if threadID is '0' # new thread # auto-noko location.pathname = "/#{g.BOARD}/res/#{postID}" else # Enable auto-posting if we have stuff to post, disable it otherwise. QR.cooldown.auto = QR.replies.length > 1 - sage = /sage/i.test reply.email - seconds = - # 300 seconds cooldown for new threads - # q: 86400 seconds - # b soc r9k: 600 seconds - if g.BOARD is 'q' - if sage - 600 - else if reply.file - 300 - else - 60 - else if sage - 60 - else - 30 - QR.cooldown.set seconds if Conf['Open Reply in New Tab'] and !g.REPLY and !QR.cooldown.auto $.open "//boards.4chan.org/#{g.BOARD}/res/#{threadID}#p#{postID}"