From 11164648e9ab399322a50ff9495b0c19343e2af4 Mon Sep 17 00:00:00 2001 From: SirStendec Date: Sun, 22 May 2016 14:08:11 -0400 Subject: [PATCH] 3.5.183. Added ban reasons to moderation cards. Cleaned up socket server subscription logic. Changed utils.prompt to let you specify your own input element. Made inverted transparent badges less harsh in normal Twitch. --- src/ember/chatview.js | 28 ++++++-- src/ember/moderation-card.js | 108 ++++++++++++++++++++++++++++-- src/ember/room.js | 80 +++++++++++++--------- src/main.js | 2 +- src/socket.js | 55 ++++++++++++--- src/styles/badges-transparent.css | 18 +++-- src/utils.js | 18 +++-- style.css | 7 +- 8 files changed, 248 insertions(+), 68 deletions(-) diff --git a/src/ember/chatview.js b/src/ember/chatview.js index 21ef860e..15b20a4d 100644 --- a/src/ember/chatview.js +++ b/src/ember/chatview.js @@ -355,6 +355,7 @@ FFZ.prototype.setup_chatview = function() { f = this; if ( Chat ) { + Chat.set('ffz_last_channel_room', Chat.get('currentChannelRoom.id')); Chat.reopen({ ffzUpdateChannels: function() { if ( ! f._chatv || f.has_bttv ) @@ -367,12 +368,29 @@ FFZ.prototype.setup_chatview = function() { }.observes("currentChannelRoom", "connectedPrivateGroupRooms"), ffzSubOwnChannelRoom: function() { - var user = f.get_user(), - room = this.get("currentChannelRoom"), - room_id = room && room.get("id"); + try { + // This logic should keep us subscribed to the current chat room + // at all times. Hopefully. + var last_room_id = this.get("ffz_last_channel_room"), + room_id = this.get("currentChannelRoom.id"), - if ( user && user.login && user.login === room_id ) - room && room.ffzSubscribe && room.ffzSubscribe(); + last_room = f.rooms && f.rooms[last_room_id], + room = f.rooms && f.rooms[room_id]; + + this.set("ffz_last_channel_room", room_id); + + f.update_room_important(last_room_id, this); + f.update_room_important(room_id, this); + + if ( last_room && ! last_room.important ) + f.ws_unsub("room." + last_room_id); + + if ( room && room.important ) + f.ws_sub("room." + room_id); + + } catch(err) { + f.error("Error updating Chat Room Subscriptions", err); + } }.observes("currentChannelRoom"), diff --git a/src/ember/moderation-card.js b/src/ember/moderation-card.js index 05b8c98c..21ac9a5f 100644 --- a/src/ember/moderation-card.js +++ b/src/ember/moderation-card.js @@ -3,6 +3,10 @@ var FFZ = window.FrankerFaceZ, constants = require("../constants"), helpers, + TO_REG = /^\/t(?:imeout)? +([^ ]+)(?: +(\d+)(?: +(.+))?)?$/, + BAN_REG = /^\/b(?:an)? +([^ ]+)(?: +(.+))?$/, + USER_REG = /\{user\}/g, + keycodes = { ESC: 27, P: 80, @@ -166,6 +170,53 @@ FFZ.settings_info.mod_card_history = { }; +FFZ.settings_info.mod_card_reasons = { + type: "button", + value: [ + "One-Man Spam", + "Posting Bad Links", + "Ban Evasion", + "Threats / Personal Info", + "Hate / Harassment", + "Ignoring Broadcaster / Moderators" + ], + + category: "Chat Moderation", + no_bttv: true, + + name: "Moderation Card Ban Reasons", + help: "Change the available options in the chat moderation card ban reasons list.", + + method: function() { + var f = this, + old_val = this.settings.mod_card_reasons.join("\n"), + input = utils.createElement('textarea'); + + input.style.marginBottom = "20px"; + + utils.prompt( + "Moderation Card Ban Reasons", + "Please enter a list of ban reasons to select from in chat moderation cards. One item per line.", + old_val, + function(new_val) { + if ( new_val === null || new_val === undefined ) + return; + + var vals = new_val.trim().split(/\s*\n\s*/g), + i = vals.length; + + while(i--) + if ( vals[i].length === 0 ) + vals.splice(i,1); + + f.settings.set('mod_card_reasons', vals); + }, + 600, input + ); + } +}; + + FFZ.settings_info.mod_buttons = { type: "button", @@ -527,6 +578,7 @@ FFZ.prototype.setup_mod_card = function() { line, is_mod = controller.get('cardInfo.isModeratorOrHigher'), + ban_reasons, chat = utils.ember_lookup('controller:chat'), user = f.get_user(), @@ -534,7 +586,12 @@ FFZ.prototype.setup_mod_card = function() { is_broadcaster = user && room_id === user.login, user_id = controller.get('cardInfo.user.id'), - alias = f.aliases[user_id]; + alias = f.aliases[user_id], + + ban_reason = function() { + return ban_reasons && ban_reasons.value ? ' ' + ban_reasons.value : ""; + }; + this.ffz_room_id = room_id; @@ -576,9 +633,28 @@ FFZ.prototype.setup_mod_card = function() { add_btn_click = function(cmd) { var user_id = controller.get('cardInfo.user.id'), cont = utils.ember_lookup('controller:chat'), - room = cont && cont.get('currentRoom'); + room = cont && cont.get('currentRoom'), - room && room.send(cmd.replace(/{user}/g, user_id), true); + cm = cmd.replace(USER_REG, user_id), + reason = ban_reason(); + + if ( reason ) { + var match = TO_REG.exec(cm); + if ( match ) { + if ( ! match[2] ) + cm += " 600"; + if ( ! match[3] ) + cm += reason; + + } else { + match = BAN_REG.exec(cm); + if ( match && ! match[2] ) { + cm += reason; + } + } + } + + room && room.send(cm, true); }, add_btn_make = function(cmd) { @@ -630,13 +706,13 @@ FFZ.prototype.setup_mod_card = function() { room = utils.ember_lookup('controller:chat').get('currentRoom'); if ( is_mod && key == keycodes.P ) - room.send("/timeout " + user_id + " 1", true); + room.send("/timeout " + user_id + " 1" + ban_reason(), true); else if ( is_mod && key == keycodes.B ) - room.send("/ban " + user_id, true); + room.send("/ban " + user_id + ban_reason(), true); else if ( is_mod && key == keycodes.T ) - room.send("/timeout " + user_id + " 600", true); + room.send("/timeout " + user_id + " 600" + ban_reason(), true); else if ( is_mod && key == keycodes.U ) room.send("/unban " + user_id, true); @@ -660,7 +736,7 @@ FFZ.prototype.setup_mod_card = function() { if ( timeout === -1 ) room.send("/unban " + user_id, true); else - room.send("/timeout " + user_id + " " + timeout, true); + room.send("/timeout " + user_id + " " + timeout + ban_reason(), true); }, btn_make = function(timeout) { @@ -700,6 +776,24 @@ FFZ.prototype.setup_mod_card = function() { this.$("button.timeout").remove(); } + + if ( f.settings.mod_card_reasons && f.settings.mod_card_reasons.length ) { + // Moderation Reasons + line = utils.createElement('div', 'extra-interface interface clearfix'); + ban_reasons = utils.createElement('select', 'ffz-ban-reasons', ''); + line.appendChild(ban_reasons); + + for(var i=0; i < f.settings.mod_card_reasons.length; i++) { + var opt = utils.createElement('option'), r = f.settings.mod_card_reasons[i]; + opt.value = r; + opt.textContent = (i+1) + ') ' + r; + ban_reasons.appendChild(opt); + } + + el.appendChild(line); + } + + var ban_btn = el.querySelector('button.ban'); if ( f.settings.mod_card_hotkeys ) ban_btn.setAttribute('title', '(B)an User'); diff --git a/src/ember/room.js b/src/ember/room.js index 870f1f74..aa95896f 100644 --- a/src/ember/room.js +++ b/src/ember/room.js @@ -554,6 +554,17 @@ FFZ.ffz_commands.help.help = "Usage: /ffz help [command]\nList available command // Room Management // -------------------- +FFZ.prototype.update_room_important = function(id, controller) { + var Chat = controller || utils.ember_lookup('controller:chat'), + room = this.rooms[id]; + + if ( ! room ) + return; + + room.important = (Chat && room.room && Chat.get('currentChannelRoom') === room.room) || (room.room && room.room.get('isGroupRoom')) || (this.settings.pinned_rooms.indexOf(id) !== -1); +}; + + FFZ.prototype.add_room = function(id, room) { if ( this.rooms[id] ) return this.log("Tried to add existing room: " + id); @@ -584,16 +595,21 @@ FFZ.prototype.add_room = function(id, room) { } } - // Let the server know where we are. - room && room.ffzSubscribe && room.ffzSubscribe(); - //this.ws_send("sub", "room." + id); + // Is the room important? + this.update_room_important(id); - // See if we need history? - if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) { - if ( ! this.ws_send("chat_history", [id,25], this._load_history.bind(this, id)) ) - data.needs_history = true; + if ( data.important ) { + // Let the server know where we are. + this.ws_sub("room." + id); + + // Do we want history? + if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) { + if ( ! this.ws_send("chat_history", [id,25], this._load_history.bind(this, id)) ) + data.needs_history = true; + } } + // Why don't we set the scrollback length, too? room.set('messageBufferSize', this.settings.scrollback_length + ((this._roomv && !this._roomv.get('stuckToBottom') && this._roomv.get('controller.model.id') === id) ? 150 : 0)); @@ -620,7 +636,7 @@ FFZ.prototype.remove_room = function(id) { utils.update_css(this._room_style, id, null); // Let the server know we're gone and delete our data for this room. - this.ws_send("unsub", "room." + id); + this.ws_unsub("room." + id); delete this.rooms[id]; // Clean up sets we aren't using any longer. @@ -918,23 +934,6 @@ FFZ.prototype._modify_room = function(room) { }.observes('r9k', 'subsOnly', 'emoteOnly', 'slow', 'ffz_banned'), - ffzShouldSubscribe: function() { - var Chat = utils.ember_lookup('controller:chat'), - room_id = this.get('id'); - - return (Chat && Chat.get('currentChannelRoom') === this) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1); - }, - - ffzSubscribe: function() { - if ( this.ffzShouldSubscribe() ) - f.ws_send("sub", "room." + this.get('id')); - }, - - ffzUnsubscribe: function(not_always) { - if ( ! not_always || ! this.ffzShouldSubscribe() ) - f.ws_send("unsub", "room." + this.get('id')); - }, - // User Level ffzUserLevel: function() { if ( this.get('isStaff') ) @@ -1100,7 +1099,7 @@ FFZ.prototype._modify_room = function(room) { last_ban.end_time = end_time; last_ban.timeouts++; - last_ban.message = message + ' ' + utils.number_commas(last_ban.timeouts) + ' times' + (!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', ')); + last_ban.message = message + ' (' + utils.number_commas(last_ban.timeouts) + ' times)' + (!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', ')); last_ban.cachedTokens = [{type: "text", text: last_ban.message}]; // Now that we've reset the tokens, if there's a line for this, @@ -1119,9 +1118,18 @@ FFZ.prototype._modify_room = function(room) { if ( ! last_ban || ! last_ban.is_delete || Math.abs(now - last_ban.date) > 15000 ) last_ban = null; - if ( last_ban ) - last_ban.cachedTokens = [message + ' ' + utils.number_commas(last_ban.timeouts) + ' times' + (last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '))]; - else { + if ( last_ban ) { + if ( reason && last_ban.reasons.indexOf(reason) === -1 ) + last_ban.reasons.push(reason); + + if ( last_ban.durations.indexOf(duration) === -1 ) + last_ban.durations.push(duration); + + last_ban.end_time = end_time; + last_ban.timeouts++; + + last_ban.cachedTokens = [message + ' (' + utils.number_commas(last_ban.timeouts) + ' times)' + (last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '))]; + } else { user_history.push({ from: 'jtv', is_delete: true, @@ -1344,11 +1352,19 @@ FFZ.prototype._modify_room = function(room) { var f_room = f.rooms && f.rooms[msg.room], ban_history = f_room && f_room.ban_history; - if ( ban_history && msg.from ) - ban_history[msg.from] = false; + if ( ban_history && msg.from ) { + // Is the last ban within 200ms? Chances are Twitch screwed up message order. + if ( ban_history[msg.from] && (new Date - ban_history[msg.from].date) <= 200 ) { + msg.ffz_deleted = true; + msg.deleted = !f.settings.prevent_clear; + + } else + ban_history[msg.from] = false; + } + // Check for message from us. - if ( ! is_whisper ) { + if ( ! is_whisper && ! msg.ffz_deleted ) { var user = f.get_user(); if ( user && user.login === msg.from ) { var was_banned = this.get('ffz_banned'); diff --git a/src/main.js b/src/main.js index a6e0c7ee..00dc18dd 100644 --- a/src/main.js +++ b/src/main.js @@ -37,7 +37,7 @@ FFZ.msg_commands = {}; // Version var VER = FFZ.version_info = { - major: 3, minor: 5, revision: 182, + major: 3, minor: 5, revision: 183, toString: function() { return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || ""); } diff --git a/src/socket.js b/src/socket.js index 5d46d04b..f8c5e9be 100644 --- a/src/socket.js +++ b/src/socket.js @@ -94,6 +94,38 @@ FFZ.settings_info.socket_server_pool = { }; +// ---------------- +// Subscription +// ---------------- + +FFZ.prototype.ws_sub = function(topic) { + this._ws_topics = this._ws_topics || {}; + if ( this._ws_topics[topic] ) + return true; + + if ( ! this._ws_open ) + return false; + + this.ws_send("sub", topic); + this._ws_topics[topic] = true; + return true; +} + + +FFZ.prototype.ws_unsub = function(topic) { + this._ws_topics = this._ws_topics || {}; + if ( ! this._ws_topics[topic] ) + return true; + + if ( ! this._ws_open ) + return true; + + this.ws_send("unsub", topic); + this._ws_topics[topic] = false; + return true; +} + + // ---------------- // Socket Creation // ---------------- @@ -104,6 +136,7 @@ FFZ.prototype.ws_create = function() { this._ws_last_req = 1; this._ws_callbacks = {1: f._ws_on_hello.bind(f)}; this._ws_pending = this._ws_pending || []; + this._ws_topics = {}; this._ws_recreate_timer = null; var pool_id = this.settings.socket_server_pool, @@ -146,9 +179,8 @@ FFZ.prototype.ws_create = function() { // Join the right channel if we're in the dashboard. if ( f.is_dashboard ) { var match = location.pathname.match(/\/([^\/]+)/); - if ( match ) { - f.ws_send("sub", "channel." + match[1]); - } + if ( match ) + f.ws_sub("channel." + match[1]); } // Send the current rooms. @@ -157,13 +189,14 @@ FFZ.prototype.ws_create = function() { if ( ! f.rooms.hasOwnProperty(room_id) || ! room ) continue; - room.room && room.room.ffzSubscribe && room.room.ffzSubscribe(); - //f.ws_send("sub", "room." + room_id); + if ( room.important ) { + f.ws_sub("room." + room_id); - if ( f.rooms[room_id].needs_history ) { - f.rooms[room_id].needs_history = false; - if ( ! f.has_bttv && f.settings.chat_history ) - f.ws_send("chat_history", [room_id,25], f._load_history.bind(f, room_id)); + if ( room.needs_history ) { + room.needs_history = false; + if ( ! f.has_bttv && f.settings.chat_history ) + f.ws_send("chat_history", [room_id,25], f._load_history.bind(f, room_id)); + } } } @@ -173,10 +206,10 @@ FFZ.prototype.ws_create = function() { hosted_id = f._cindex.get('controller.hostModeTarget.id'); if ( channel_id ) - f.ws_send("sub", "channel." + channel_id); + f.ws_sub("channel." + channel_id); if ( hosted_id ) - f.ws_send("sub", "channel." + hosted_id); + f.ws_sub("channel." + hosted_id); } // Send any pending commands. diff --git a/src/styles/badges-transparent.css b/src/styles/badges-transparent.css index 84dc1e35..92294ef6 100644 --- a/src/styles/badges-transparent.css +++ b/src/styles/badges-transparent.css @@ -4,11 +4,15 @@ /* Invert Some Badges */ -body:not(.ffz-dark) .app-main:not(.theatre) .conversation-window .badges .badge:not(.subscriber):not(.ffz-badge-1), -body:not(.ffz-dark) > .chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-1), -body:not(.ffz-dark) > .ember-chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-1), -.app-main:not(.theatre) .chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-1), -.app-main:not(.theatre) .ember-chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-1) { - filter: invert(100%); - -webkit-filter: invert(100%); +.badge:not(.subscriber):not(.ffz-badge-1) { + filter: invert(75%); + -webkit-filter: invert(75%); +} + +.ffz-dark .badge, +.theatre .badge, +.dark .badge, +.force-dark .badge { + filter: none !important; + -webkit-filter: none !important; } \ No newline at end of file diff --git a/src/utils.js b/src/utils.js index d141d9f6..76db9267 100644 --- a/src/utils.js +++ b/src/utils.js @@ -258,22 +258,32 @@ module.exports = FFZ.utils = { show_modal: show_modal, - prompt: function(title, description, old_value, callback, width) { + prompt: function(title, description, old_value, callback, width, input) { var contents = document.createElement('div'), heading = document.createElement('div'), form = document.createElement('form'), - input, close_btn, okay_btn; + close_btn, okay_btn; contents.className = 'text-content'; heading.className = 'content-header'; heading.innerHTML = '

' + title + '

'; - form.innerHTML = '
' + (description ? '

' + description + '

' : '') + '
Cancel
'; + if ( ! input ) { + input = document.createElement('input'); + input.type = 'text'; + } + + form.innerHTML = '
' + (description ? '

' + description + '

' : '') + '
'; + + var ph = form.querySelector('.input-placeholder'), + par = ph.parentElement; + + par.insertBefore(input, ph); + par.removeChild(ph); contents.appendChild(heading); contents.appendChild(form); - input = form.querySelector('input'); close_btn = form.querySelector('.js-subwindow-close'); okay_btn = form.querySelector('.button.primary'); diff --git a/style.css b/style.css index 4a4361ce..4fc4e9c5 100644 --- a/style.css +++ b/style.css @@ -1248,7 +1248,7 @@ img.channel_background[src="null"] { display: none; } .ffz-moderation-card .friend-button { max-height: 30px } .ffz-moderation-card .right button:last-of-type, -.ffz-moderation-card .mod-controls button:last-of-type { margin-right: 0 } +.ffz-moderation-card .mod-controls:last-of-type button:last-of-type { margin-right: 0 } .ffz-moderation-card .follow-button a { text-indent: -9999px; @@ -1298,6 +1298,11 @@ img.channel_background[src="null"] { display: none; } top: 0; } +.ffz-moderation-card .ffz-ban-reasons { + margin-top: 10px; + width: 100%; +} + /* dark moderation card */