mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
3.5.330. Refactor timeout and ban notices. Add the ability to click the name of banned users in timeout notices. Fix logviewer dynamic updates. Reformat logviewer ban notices to match the rest of FFZ. Fix a memory leak in room. Fix an infinite loop in notifications.
This commit is contained in:
parent
13efe21492
commit
36f0b33d04
13 changed files with 389 additions and 260 deletions
|
@ -1,3 +1,11 @@
|
|||
<div class="list-header">3.5.330 <time datetime="2016-10-14">(2016-10-14)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Click the user's name in timeout and ban notices to open their moderation card.</li>
|
||||
<li>Changed: Refactor how timeout and ban notices are rendered.</li>
|
||||
<li>Fixed: Gradual memory leak in chat keeping old message objects around.</li>
|
||||
<li>Fixed: Attempting to display a notification after the user has specifically denied the permission would create an infinite loop.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.329 <time datetime="2016-10-14">(2016-10-14)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Issue causing Ember to stop responding to observers regarding chat messages.</li>
|
||||
|
|
|
@ -102,7 +102,7 @@ FFZ.prototype.setup_channel = function() {
|
|||
id = target && target.get('id'),
|
||||
display_name = target && target.get('display_name');
|
||||
|
||||
if ( display_name )
|
||||
if ( display_name && display_name !== 'jtv' )
|
||||
FFZ.capitalization[name] = [display_name, Date.now()];
|
||||
|
||||
if ( f._chatv )
|
||||
|
|
|
@ -52,7 +52,7 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
if ( ! user || ! user.name )
|
||||
continue;
|
||||
|
||||
if ( user.display_name )
|
||||
if ( user.display_name && user.display_name !== 'jtv' )
|
||||
FFZ.capitalization[user.name] = [user.display_name, now];
|
||||
|
||||
user_cache[user.name] = [
|
||||
|
|
|
@ -1163,7 +1163,13 @@ FFZ.prototype._modify_chat_subline = function(component) {
|
|||
} else if ( f._click_emote(e.target, e) )
|
||||
return;
|
||||
|
||||
else if ( e.target.classList.contains('from') || e.target.parentElement.classList.contains('from') ) {
|
||||
else if ( cl.contains('ban-target') || cl.contains('from') || cl.contains('to') || e.target.parentElement.classList.contains('from') || e.target.parentElement.classList.contains('to') ) {
|
||||
var target = cl.contains('ban-target') ?
|
||||
e.target.getAttribute('data-user') :
|
||||
(cl.contains('from') || e.target.parentElement.classList.contains('from')) ?
|
||||
from :
|
||||
this.get('msgObject.to');
|
||||
|
||||
var n = this.get('element'),
|
||||
bounds = n && n.getBoundingClientRect() || document.body.getBoundingClientRect(),
|
||||
x = 0, right;
|
||||
|
@ -1176,26 +1182,10 @@ FFZ.prototype._modify_chat_subline = function(component) {
|
|||
right: right,
|
||||
top: bounds.top + bounds.height,
|
||||
real_top: bounds.top,
|
||||
sender: from
|
||||
sender: target
|
||||
});
|
||||
|
||||
} else if ( e.target.classList.contains('to') || e.target.parentElement.classList.contains('to') ) {
|
||||
var n = this.get('element'),
|
||||
bounds = n && n.getBoundingClientRect() || document.body.getBoundingClientRect(),
|
||||
x = 0, right;
|
||||
|
||||
if ( bounds.left > 400 )
|
||||
right = bounds.left - 40;
|
||||
|
||||
this.sendAction("showModOverlay", {
|
||||
left: bounds.left,
|
||||
right: right,
|
||||
top: bounds.top + bounds.height,
|
||||
real_top: bounds.top,
|
||||
sender: this.get('msgObject.to')
|
||||
});
|
||||
|
||||
} else if ( e.target.classList.contains('undelete') ) {
|
||||
} else if ( cl.contains('undelete') ) {
|
||||
e.preventDefault();
|
||||
this.set("msgObject.deleted", false);
|
||||
}
|
||||
|
|
|
@ -655,7 +655,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
|||
user_id = this.get('cardInfo.user.id');
|
||||
|
||||
if (( this._lv_sock_room && this._lv_sock_room !== room_id ) || (this._lv_sock_user && this._lv_sock_user !== user_id) ) {
|
||||
f.lv_ws_unsub(this._lv_sock_room + '-' + this._lv_sock_user);
|
||||
f.lv_ws_unsub('logs-' + this._lv_sock_room + '-' + this._lv_sock_user);
|
||||
this._lv_sock_room = null;
|
||||
this._lv_sock_user = null;
|
||||
}
|
||||
|
@ -738,7 +738,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
|||
|
||||
t._lv_sock_room = room_id;
|
||||
t._lv_sock_user = user_id;
|
||||
f.lv_ws_sub(room_id + '-' + user_id);
|
||||
f.lv_ws_sub('logs-' + room_id + '-' + user_id);
|
||||
|
||||
var requests = t._lv_log_requests;
|
||||
t._lv_log_requests = null;
|
||||
|
@ -785,13 +785,48 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
|||
// Remove the line itself.
|
||||
line.parentElement.removeChild(line);
|
||||
|
||||
} else if ( cmd === "log-update" ) {
|
||||
if ( ! this._lv_logs || ! this._lv_logs.data || data.nick !== this._lv_logs.data.user.nick )
|
||||
return;
|
||||
|
||||
// Parse the message. Store the data.
|
||||
var message = f.lv_parse_message(data),
|
||||
msgs = this._lv_logs.data.before,
|
||||
ind = -1,
|
||||
i = msgs.length;
|
||||
|
||||
// Find the existing entry.
|
||||
while(--i) {
|
||||
var msg = msgs[i];
|
||||
if ( msg.lv_id === message.lv_id ) {
|
||||
ind = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing to update, so don't.
|
||||
if ( ind === -1 )
|
||||
return;
|
||||
|
||||
msgs[ind] = message;
|
||||
var el = this.get('element'),
|
||||
container = el && el.querySelector('.ffz-tab-container'),
|
||||
line = container && container.querySelector('.lv-history .chat-line[data-lv-id="' + message.lv_id + '"]');
|
||||
|
||||
if ( ! line )
|
||||
return;
|
||||
|
||||
var new_line = f._build_mod_card_history(message, this, false,
|
||||
FFZ.mod_card_pages.history.render_adjacent.bind(f, this, container, message));
|
||||
line.parentElement.insertBefore(new_line, line);
|
||||
line.parentElement.removeChild(line);
|
||||
|
||||
} else if ( cmd === "log-add" ) {
|
||||
if ( ! this._lv_logs || ! this._lv_logs.data || data.nick !== this._lv_logs.data.user.nick )
|
||||
return;
|
||||
|
||||
// Parse the message. Store the data.
|
||||
var t,
|
||||
message = f.lv_parse_message(data);
|
||||
var message = f.lv_parse_message(data);
|
||||
this._lv_logs.data.before.push(message);
|
||||
this._lv_logs.data.user[message.is_ban ? 'timeouts' : 'messages'] += 1;
|
||||
|
||||
|
@ -819,7 +854,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
|||
}
|
||||
|
||||
history.appendChild(f._build_mod_card_history(message, this, false,
|
||||
FFZ.mod_card_pages.history.render_adjacent.bind(f, t, container, message)));
|
||||
FFZ.mod_card_pages.history.render_adjacent.bind(f, this, container, message)));
|
||||
|
||||
if ( was_at_bottom )
|
||||
setTimeout(function() { history.scrollTop = history.scrollHeight; })
|
||||
|
@ -852,7 +887,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
|
|||
f._mod_card = undefined;
|
||||
|
||||
if ( this._lv_sock_room && this._lv_sock_user ) {
|
||||
f.lv_ws_unsub(this._lv_sock_room + '-' + this._lv_sock_user);
|
||||
f.lv_ws_unsub('logs-' + this._lv_sock_room + '-' + this._lv_sock_user);
|
||||
this._lv_sock_room = null;
|
||||
this._lv_sock_user = null;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ var FFZ = window.FrankerFaceZ,
|
|||
'emoteonly': 'emote_only_on',
|
||||
'emoteonlyoff': 'emote_only_off',
|
||||
'host': 'host_on',
|
||||
'unhost': 'host_off'
|
||||
'unhost': 'host_off',
|
||||
'clear': 'clear_chat'
|
||||
},
|
||||
|
||||
STATUS_BADGES = [
|
||||
|
@ -161,6 +162,31 @@ FFZ.prototype.setup_room = function() {
|
|||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Ban Message Formatting
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.format_ban_notice = function(username, is_me, duration, count, reasons, moderators, notices) {
|
||||
var name = this.format_display_name(FFZ.get_capitalization(username), username, true),
|
||||
duration_tip = [];
|
||||
|
||||
for(var mod_id in notices) {
|
||||
var notice = notices[mod_id];
|
||||
if ( ! Array.isArray(notice) )
|
||||
notice = [notice];
|
||||
|
||||
var nd = notice[0] === -Infinity ? 'unban' : isFinite(notice[0]) ? utils.duration_string(notice[0], true) : 'ban';
|
||||
duration_tip.push(utils.sanitize(mod_id) + ' - ' + nd + (notice[1] ? ': ' + utils.sanitize(notice[1]) : ''));
|
||||
}
|
||||
|
||||
return (is_me ? 'You have' : '<span data-user="' + utils.quote_san(username) + '" class="ban-target html-tooltip" title="' + utils.quote_attr(name[1] || '') + '">' + name[0] + '</span> has') +
|
||||
' been ' + (duration_tip.length ? '<span class="ban-tip html-tooltip" title="' + utils.quote_attr(duration_tip.join('<br>')) + '">' : '') + (duration === -Infinity ? 'unbanned' :
|
||||
(duration === 1 ? 'purged' : isFinite(duration) ? 'timed out for ' + utils.duration_string(duration) : 'banned')) +
|
||||
(count > 1 ? ' (' + utils.number_commas(count) + ' times)' : '') +
|
||||
(moderators && moderators.length ? ' by ' + utils.sanitize(moderators.join(', ')) : '') + (duration_tip.length ? '</span>' : '') +
|
||||
(reasons && reasons.length ? ' with reason' + utils.pluralize(reasons.length) + ': ' + utils.sanitize(reasons.join(', ')) : '.');
|
||||
}
|
||||
|
||||
// --------------------
|
||||
// PubSub is fucking awful
|
||||
// --------------------
|
||||
|
@ -263,17 +289,11 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
|
|||
return;
|
||||
|
||||
var ps = pubsub._pubsub(),
|
||||
token = user.chat_oauth_token;
|
||||
token = user.chat_oauth_token,
|
||||
internal_topics = ps._client & ps._client._listens && ps._client._listens._events || {};
|
||||
|
||||
for(var i=0; i < pubsub.chatTopics.length; i++) {
|
||||
var topic = pubsub.chatTopics[i];
|
||||
// Try stupid stuff to remove duplicate events.
|
||||
if ( topic.substr(0, 23) === 'chat_moderator_actions.' )
|
||||
ps.Unlisten({
|
||||
topic: topic.split('.', 2).join('.'),
|
||||
success: function() {},
|
||||
failure: function() {}
|
||||
});
|
||||
|
||||
ps.Unlisten({
|
||||
topic: topic,
|
||||
|
@ -281,6 +301,14 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
|
|||
failure: function(topic, t) { f.log("[PubSub] Failed to unlisten to topic: " + topic, t); }.bind(this, topic)
|
||||
});
|
||||
|
||||
// Find the event and manually remove our listeners. We still want the topic so
|
||||
// we don't clean it up too much, but we need to get rid of the existing bound
|
||||
// functions to avoid anything screwing up.
|
||||
var it = internal_topics[topic];
|
||||
if ( it && it.length )
|
||||
it.splice(0, it.length);
|
||||
|
||||
// Now, register our own event handler.
|
||||
ps.Listen({
|
||||
topic: topic,
|
||||
auth: token,
|
||||
|
@ -1306,29 +1334,173 @@ FFZ.prototype._modify_room = function(room) {
|
|||
},
|
||||
|
||||
addLoginModerationMessage: function(event) {
|
||||
// Throw out messages that are for other rooms.
|
||||
// Throw out messages that are for other rooms or that don't have topics.
|
||||
var room_id = '.' + this.get("roomProperties._id");
|
||||
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
|
||||
if ( ! event.topic || event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
|
||||
return;
|
||||
|
||||
// f.log("Login Moderation", event);
|
||||
//f.log("Login Moderation for " + this.get('id') + ' [' + room_id + ']', event);
|
||||
|
||||
// In case we get unexpected input, do the other thing.
|
||||
if ( ["ban", "unban", "timeout"].indexOf(event.moderation_action) === -1 )
|
||||
return this._super(event);
|
||||
|
||||
var tags = {
|
||||
'ban-duration': event.moderation_action === 'unban' ? -Infinity : event.args[1],
|
||||
'ban-reason': event.args[2],
|
||||
'ban-moderator': event.created_by
|
||||
};
|
||||
var msg_id,
|
||||
reason = event.args[2],
|
||||
duration = event.moderator_action === 'unban' ? -Infinity : event.args[1];
|
||||
|
||||
this.clearMessages(event.args[0].toLowerCase(), tags, false, event.moderation_action !== 'unban');
|
||||
if ( typeof duration === "string" )
|
||||
duration = parseInt(duration);
|
||||
|
||||
if ( isNaN(duration) )
|
||||
duration = Infinity;
|
||||
|
||||
if ( reason ) {
|
||||
var match = constants.UUID_TEST.exec(reason);
|
||||
if ( match ) {
|
||||
msg_id = match[1];
|
||||
reason = reason.substr(0, reason.length - match[0].length);
|
||||
if ( ! reason.length )
|
||||
reason = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.addBanNotice(event.args[0].toLowerCase(), duration, reason, event.created_by, msg_id, true);
|
||||
},
|
||||
|
||||
clearMessages: function(user, tags, disable_log, report_only) {
|
||||
var t = this;
|
||||
addBanNotice: function(username, duration, reason, moderator, msg_id, report_only) {
|
||||
var current_user = f.get_user(),
|
||||
is_me = current_user && current_user.login === username,
|
||||
|
||||
show_notice = is_me || this.ffzShouldDisplayNotice(),
|
||||
show_reason = is_me || this.get('isModeratorOrHigher'),
|
||||
show_moderator = f.settings.get_twitch('showModerationActions'),
|
||||
|
||||
now = new Date,
|
||||
room_id = this.get('id'),
|
||||
ffz_room = f.rooms[room_id],
|
||||
ban_history, last_ban;
|
||||
|
||||
// Find an existing ban to modify.
|
||||
if ( ffz_room ) {
|
||||
var ban_history = ffz_room.ban_history = ffz_room.ban_history || {};
|
||||
last_ban = ban_history[username];
|
||||
|
||||
// Only overwrite bans in the last 15 seconds.
|
||||
if ( ! last_ban || Math.abs(now - last_ban.date) > 15000 )
|
||||
last_ban = null;
|
||||
}
|
||||
|
||||
// If we have an existing ban, modify that.
|
||||
if ( last_ban ) {
|
||||
if ( reason && last_ban.reasons.indexOf(reason) === -1 )
|
||||
last_ban.reasons.push(reason);
|
||||
|
||||
if ( moderator && last_ban.moderators.indexOf(moderator) === -1 )
|
||||
last_ban.moderators.push(moderator);
|
||||
|
||||
if ( moderator )
|
||||
last_ban.notices[moderator] = [duration, reason];
|
||||
|
||||
if ( ! report_only )
|
||||
last_ban.count++;
|
||||
|
||||
// Don't update the displayed duration if the new end time is within five
|
||||
// seconds to avoid changing messages when bots do multiple timeouts.
|
||||
var end_time = now.getTime() + (duration * 1000);
|
||||
if ( Math.abs(end_time - last_ban.end_time) > 5000 ) {
|
||||
last_ban.duration = duration;
|
||||
last_ban.end_time = end_time;
|
||||
} else
|
||||
duration = last_ban.duration;
|
||||
|
||||
last_ban.message = f.format_ban_notice(username, is_me, duration, last_ban.count, show_reason && last_ban.reasons, show_moderator && last_ban.moderators, show_moderator && last_ban.notices);
|
||||
last_ban.cachedTokens = [{type: "raw", html: last_ban.message}];
|
||||
|
||||
if ( last_ban._line )
|
||||
Ember.propertyDidChange(last_ban._line, 'ffzTokenizedMessage');
|
||||
|
||||
} else {
|
||||
var notices = {};
|
||||
if ( moderator )
|
||||
notices[moderator] = [duration, reason];
|
||||
|
||||
var count = report_only ? 0 : 1,
|
||||
msg = f.format_ban_notice(username, is_me, duration, count, show_reason && reason && [reason], show_moderator && moderator && [moderator], show_moderator && notices),
|
||||
message = {
|
||||
style: 'admin',
|
||||
date: now,
|
||||
room: room_id,
|
||||
ffz_ban_target: username,
|
||||
reasons: reason ? [reason] : [],
|
||||
moderators: moderator ? [moderator] : [],
|
||||
notices: notices,
|
||||
duration: duration,
|
||||
end_time: now.getTime() + (duration * 1000),
|
||||
count: count,
|
||||
message: msg,
|
||||
cachedTokens: [{type: "raw", html: msg}]
|
||||
};
|
||||
|
||||
if ( ban_history )
|
||||
ban_history[username] = message;
|
||||
|
||||
if ( show_notice )
|
||||
this.addMessage(message);
|
||||
|
||||
this.addUserHistory(message);
|
||||
}
|
||||
},
|
||||
|
||||
addUserHistory: function(message) {
|
||||
var room_id = this.get('id'),
|
||||
ffz_room = f.rooms[room_id];
|
||||
|
||||
if ( ! ffz_room || ! f.settings.mod_card_history )
|
||||
return;
|
||||
|
||||
var username = message.ffz_ban_target || message.from,
|
||||
historical = message.tags && message.tags.historical,
|
||||
|
||||
chat_history = ffz_room.user_history = ffz_room.user_history || {},
|
||||
user_history = chat_history[username] = chat_history[username] || [];
|
||||
|
||||
if ( historical ) {
|
||||
if ( user_history.length >= 20 )
|
||||
return;
|
||||
|
||||
user_history.unshift(message);
|
||||
|
||||
} else {
|
||||
user_history.push(message);
|
||||
while ( user_history.length > 20 )
|
||||
user_history.shift();
|
||||
}
|
||||
|
||||
if ( f._mod_card && f._mod_card.ffz_room_id === room_id && f._mod_card.get('cardInfo.user.id') === username) {
|
||||
var el = f._mod_card.get('element'),
|
||||
history = el && el.querySelector('.chat-history.live-history');
|
||||
|
||||
if ( history ) {
|
||||
var was_at_top = history.scrollTop >= (history.scrollHeight - history.clientHeight),
|
||||
line = f._build_mod_card_history(message, f._mod_card);
|
||||
|
||||
if ( historical )
|
||||
history.insertBefore(line, history.firstElementChild);
|
||||
else
|
||||
history.appendChild(line);
|
||||
|
||||
if ( was_at_top )
|
||||
setTimeout(function() { history.scrollTop = history.scrollHeight });
|
||||
|
||||
if ( history.childElementCount > 20 )
|
||||
history.removeChild(history.firstElementChild);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clearMessages: function(user, tags, disable_log) {
|
||||
var t = this;
|
||||
if ( user ) {
|
||||
var duration = Infinity,
|
||||
reason = undefined,
|
||||
|
@ -1353,10 +1525,6 @@ FFZ.prototype._modify_room = function(room) {
|
|||
if ( tags && tags['ban-moderator'] && (is_me || t.get('isModeratorOrHigher')) )
|
||||
moderator = tags['ban-moderator'];
|
||||
|
||||
|
||||
// Does anything really matter?
|
||||
if ( ! report_only && duration !== -Infinity ) {
|
||||
|
||||
// Is there a UUID on the end of the ban reason?
|
||||
if ( reason ) {
|
||||
var match = constants.UUID_TEST.exec(reason);
|
||||
|
@ -1368,19 +1536,18 @@ FFZ.prototype._modify_room = function(room) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// If we were banned, set the state and update the UI.
|
||||
if ( is_me ) {
|
||||
t.set('ffz_banned', true);
|
||||
if ( typeof duration === "number" && duration && isFinite(duration) && !isNaN(duration) )
|
||||
t.updateWait(duration)
|
||||
else if ( duration ) {
|
||||
t.set('slowWait', 0);
|
||||
f._roomv && f._roomv.ffzUpdateStatus();
|
||||
}
|
||||
if ( duration )
|
||||
if ( isFinite(duration) )
|
||||
t.updateWait(duration);
|
||||
else {
|
||||
t.set('slowWait', 0);
|
||||
f._roomv && f._roomv.ffzUpdateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mark the user as recently banned.
|
||||
if ( ! t.ffzRecentlyBanned )
|
||||
t.ffzRecentlyBanned = [];
|
||||
|
@ -1389,7 +1556,6 @@ FFZ.prototype._modify_room = function(room) {
|
|||
while ( t.ffzRecentlyBanned.length > 100 )
|
||||
t.ffzRecentlyBanned.shift();
|
||||
|
||||
|
||||
// Are we deleting a specific message?
|
||||
if ( msg_id && this.ffz_ids ) {
|
||||
var msg = this.ffz_ids[msg_id];
|
||||
|
@ -1479,143 +1645,27 @@ FFZ.prototype._modify_room = function(room) {
|
|||
}
|
||||
}
|
||||
|
||||
// End of report_only check.
|
||||
}
|
||||
|
||||
|
||||
// Now we need to see about displaying a ban notice.
|
||||
if ( ! disable_log ) {
|
||||
// Look up the user's last ban.
|
||||
var show_notice = is_me || this.ffzShouldDisplayNotice(),
|
||||
show_reason = is_me || this.get('isModeratorOrHigher'),
|
||||
show_moderator = f.settings.get_twitch('showModerationActions'),
|
||||
room = f.rooms && f.rooms[t.get('id')],
|
||||
now = new Date,
|
||||
end_time = now + (duration * 1000),
|
||||
ban_history, last_ban;
|
||||
|
||||
if ( room ) {
|
||||
ban_history = room.ban_history = room.ban_history || {};
|
||||
last_ban = ban_history[user];
|
||||
|
||||
// Only overwrite a ban in the last 15 seconds.
|
||||
if ( ! last_ban || Math.abs(now - last_ban.date) > 15000 )
|
||||
last_ban = null;
|
||||
}
|
||||
|
||||
// Display a notice in chat.
|
||||
var message = (is_me ?
|
||||
"You have" : ffz.format_display_name(FFZ.get_capitalization(user), user, true, false, true)[0] + " has") +
|
||||
" been " + (duration === -Infinity ? 'unbanned' :
|
||||
(duration === 1 ? 'purged' :
|
||||
(isFinite(duration) ? "timed out for " + utils.duration_string(duration, true) : "banned")));
|
||||
|
||||
if ( show_notice ) {
|
||||
if ( ! last_ban ) {
|
||||
var msg = {
|
||||
style: "admin",
|
||||
date: now,
|
||||
ffz_ban_target: user,
|
||||
reasons: reason ? [reason] : [],
|
||||
moderators: moderator ? [moderator] : [],
|
||||
msg_ids: msg_id ? [msg_id] : [],
|
||||
durations: [duration],
|
||||
end_time: end_time,
|
||||
timeouts: report_only ? 0 : 1,
|
||||
message: message + (show_reason && show_moderator && moderator ? ' by ' + moderator : '') + (show_reason && reason ? ' with reason: ' + reason : '.')
|
||||
};
|
||||
|
||||
if ( ban_history )
|
||||
ban_history[user] = msg;
|
||||
|
||||
this.addMessage(msg);
|
||||
|
||||
} else {
|
||||
if ( msg_id && last_ban.msg_ids.indexOf(msg_id) === -1 )
|
||||
last_ban.msg_ids.push(msg_id);
|
||||
|
||||
if ( reason && last_ban.reasons.indexOf(reason) === -1 )
|
||||
last_ban.reasons.push(reason);
|
||||
|
||||
if ( moderator && last_ban.moderators.indexOf(moderator) === -1 )
|
||||
last_ban.moderators.push(moderator);
|
||||
|
||||
if ( last_ban.durations.indexOf(duration) === -1 )
|
||||
last_ban.durations.push(duration);
|
||||
|
||||
last_ban.end_time = end_time;
|
||||
|
||||
if ( ! report_only )
|
||||
last_ban.timeouts++;
|
||||
|
||||
last_ban.message = message +
|
||||
(last_ban.timeouts > 1 ? ' (' + utils.number_commas(last_ban.timeouts) + ' times)' : '') +
|
||||
(!show_reason || !show_moderator || last_ban.moderators.length === 0 ? '' : ' by ' + last_ban.moderators.join(', ') ) +
|
||||
(!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,
|
||||
if ( last_ban._line )
|
||||
Ember.propertyDidChange(last_ban._line, 'ffzTokenizedMessage');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Mod Card History
|
||||
if ( room && f.settings.mod_card_history ) {
|
||||
var chat_history = room.user_history = room.user_history || {},
|
||||
user_history = room.user_history[user] = room.user_history[user] || [],
|
||||
last_ban = user_history.length > 0 ? user_history[user_history.length-1] : null;
|
||||
|
||||
if ( ! last_ban || ! last_ban.is_delete || Math.abs(now - last_ban.date) > 15000 )
|
||||
last_ban = null;
|
||||
|
||||
if ( last_ban ) {
|
||||
if ( msg_id && last_ban.msg_ids.indexOf(msg_id) === -1 )
|
||||
last_ban.msg_ids.push(msg_id);
|
||||
|
||||
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,
|
||||
style: 'admin',
|
||||
date: now,
|
||||
ffz_ban_target: user,
|
||||
reasons: reason ? [reason] : [],
|
||||
msg_ids: msg_id ? [msg_id] : [],
|
||||
durations: [duration],
|
||||
end_time: end_time,
|
||||
timeouts: 1,
|
||||
cachedTokens: message + (reason ? ' with reason: ' + reason : '.')
|
||||
})
|
||||
|
||||
while ( user_history.length > 20 )
|
||||
user_history.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! disable_log )
|
||||
this.addBanNotice(user, duration, reason, null, msg_id);
|
||||
|
||||
} else {
|
||||
if ( f.settings.prevent_clear )
|
||||
this.addTmiMessage("A moderator's attempt to clear chat was ignored.");
|
||||
t.addMessage({
|
||||
style: 'admin',
|
||||
message: "A moderator's attempt to clear chat was ignored.",
|
||||
tags: {
|
||||
'msg-id': 'clear_chat'
|
||||
}
|
||||
});
|
||||
else {
|
||||
var msgs = t.get("messages");
|
||||
t.set("messages", []);
|
||||
t.addMessage({
|
||||
style: 'admin',
|
||||
message: i18n("Chat was cleared by a moderator")
|
||||
//ffz_old_messages: msgs
|
||||
message: i18n("Chat was cleared by a moderator"),
|
||||
tags: {
|
||||
'msg-id': 'clear_chat'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1679,8 +1729,22 @@ FFZ.prototype._modify_room = function(room) {
|
|||
Math.max(0, room_messages.length - this.messageBufferSize) + new_messages.length : 0,
|
||||
|
||||
to_remove = raw_remove - raw_remove % 2,
|
||||
removed = room_messages.slice(0, to_remove),
|
||||
trimmed = room_messages.slice(to_remove, room_messages.length);
|
||||
|
||||
// Garbage collect removed messages.
|
||||
for(var i=0; i < removed.length; i++) {
|
||||
var msg = removed[i],
|
||||
msg_id = msg.tags && msg.tags.id,
|
||||
notice_type = msg.tags && msg.tags['msg-id'];
|
||||
|
||||
if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
|
||||
delete this.ffz_ids[msg_id];
|
||||
|
||||
if ( notice_type && this.ffz_last_notices && this.ffz_last_notices[notice_type] === msg )
|
||||
delete this.ffz_last_notices[notice_type];
|
||||
}
|
||||
|
||||
var earliest_message;
|
||||
for(var i=0; i < trimmed.length; i++)
|
||||
if ( trimmed[i].date ) {
|
||||
|
@ -1892,6 +1956,10 @@ FFZ.prototype._modify_room = function(room) {
|
|||
msg.labels = this.tmiRoom.getLabels(msg.from);
|
||||
}
|
||||
|
||||
// Tag the broadcaster.
|
||||
if ( room_id === msg.from )
|
||||
msg.tags.mod = true;
|
||||
|
||||
// Tokenization
|
||||
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
|
||||
|
||||
|
@ -1935,63 +2003,19 @@ FFZ.prototype._modify_room = function(room) {
|
|||
|
||||
|
||||
// Keep the history.
|
||||
if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_history ) {
|
||||
var room = f.rooms && f.rooms[msg.room];
|
||||
if ( room ) {
|
||||
var chat_history = room.user_history = room.user_history || {},
|
||||
user_history = room.user_history[msg.from] = room.user_history[msg.from] || [],
|
||||
last_history = user_history.length && user_history[user_history.length - 1],
|
||||
|
||||
new_msg = {
|
||||
from: msg.from,
|
||||
tags: {
|
||||
'display-name': msg.tags && msg.tags['display-name'],
|
||||
bits: msg.tags && msg.tags.bits
|
||||
},
|
||||
message: msg.message,
|
||||
cachedTokens: msg.cachedTokens,
|
||||
style: msg.style,
|
||||
date: msg.date
|
||||
};
|
||||
|
||||
if ( msg.tags && msg.tags.historical ) {
|
||||
// If it's historical, insert it at the beginning. And stuff.
|
||||
if ( user_history.length < 20 )
|
||||
user_history.unshift(new_msg);
|
||||
|
||||
} else {
|
||||
// Preserve message order if we *just* received a ban.
|
||||
if ( last_history && last_history.is_delete && (msg.date - last_history.date) <= 200 ) {
|
||||
user_history.splice(user_history.length - 1, 0, new_msg);
|
||||
} else
|
||||
user_history.push(new_msg);
|
||||
|
||||
if ( user_history.length > 20 )
|
||||
user_history.shift();
|
||||
}
|
||||
|
||||
if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
|
||||
var el = f._mod_card.get('element'),
|
||||
history = el && el.querySelector('.chat-history.live-history');
|
||||
|
||||
if ( history ) {
|
||||
var was_at_top = history.scrollTop >= (history.scrollHeight - history.clientHeight),
|
||||
el = f._build_mod_card_history(msg, f._mod_card);
|
||||
|
||||
if ( msg.tags && msg.tags.historical )
|
||||
history.insertBefore(el, history.firstElementChild);
|
||||
else
|
||||
history.appendChild(el);
|
||||
if ( was_at_top )
|
||||
setTimeout(function() { history.scrollTop = history.scrollHeight; })
|
||||
|
||||
// Don't do infinite scrollback.
|
||||
if ( history.childElementCount > 100 )
|
||||
history.removeChild(history.firstElementChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' )
|
||||
this.addUserHistory({
|
||||
from: msg.from,
|
||||
tags: {
|
||||
id: msg.tags && msg.tags.id,
|
||||
'display-name': msg.tags && msg.tags['display-name'],
|
||||
bits: msg.tags && msg.tags.bits
|
||||
},
|
||||
message: msg.message,
|
||||
cachedTokens: msg.cachedTokens,
|
||||
style: msg.style,
|
||||
date: msg.date
|
||||
});
|
||||
|
||||
// Clear the last ban for that user.
|
||||
var f_room = f.rooms && f.rooms[msg.room],
|
||||
|
|
|
@ -54,7 +54,7 @@ FFZ.prototype.modify_viewer_list = function(component) {
|
|||
// We can get capitalization for the broadcaster from the channel.
|
||||
if ( Channel && Channel.get('channelModel.id') === room_id ) {
|
||||
var display_name = Channel.get('channelModel.displayName');
|
||||
if ( display_name )
|
||||
if ( display_name && display_name !== 'jtv' )
|
||||
FFZ.capitalization[broadcaster] = [display_name, Date.now()];
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ FFZ.channel_metadata = {};
|
|||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 329,
|
||||
major: 3, minor: 5, revision: 330,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
|
|
@ -575,7 +575,7 @@ FFZ.prototype.tokenize_conversation_line = function(message, prevent_notificatio
|
|||
|
||||
// Capitalization
|
||||
var display_name = message.get('from.displayName');
|
||||
if ( display_name && display_name.length )
|
||||
if ( display_name && display_name.length && display_name !== 'jtv' )
|
||||
FFZ.capitalization[from_user] = [display_name.trim(), Date.now()];
|
||||
|
||||
// Mentions!
|
||||
|
@ -626,7 +626,7 @@ FFZ.prototype.tokenize_vod_line = function(msgObject, delete_links) {
|
|||
tokens = this.tokenize_emoji(tokens);
|
||||
|
||||
var display = msgObject.get('tags.display-name');
|
||||
if ( display && display.length )
|
||||
if ( display && display.length && display !== 'jtv' )
|
||||
FFZ.capitalization[from_user] = [display.trim(), Date.now()];
|
||||
|
||||
if ( ! from_me ) {
|
||||
|
@ -709,7 +709,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
|
||||
// Capitalization
|
||||
var display = tags['display-name'];
|
||||
if ( display && display.length )
|
||||
if ( display && display.length && display !== 'jtv' )
|
||||
FFZ.capitalization[from_user] = [display.trim(), Date.now()];
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
constants = require('../constants'),
|
||||
|
||||
BAN_REGEX = /^<([^ ]+) has been (banned|timed out|unbanned)(?: for ([^.]+))?\.?(?: Reasons?: ([^(>]+))?(?: ?\((\d+) times\))?>$/;
|
||||
|
||||
|
||||
// ----------------
|
||||
|
@ -90,12 +92,35 @@ FFZ.prototype.lv_parse_message = function(message) {
|
|||
room = ffz_room && ffz_room.room;
|
||||
|
||||
parsed.lv_id = message.id;
|
||||
parsed.date = new Date(message.time * 1000);
|
||||
parsed.mod_logs = message.modlog;
|
||||
parsed.date = typeof message.time === "number" ? new Date(message.time * 1000) : utils.parse_date(message.time);
|
||||
|
||||
// Check for ban notices. Those are identifiable via display-name.
|
||||
parsed.is_ban = parsed.tags['display-name'] === 'jtv';
|
||||
if ( parsed.is_ban )
|
||||
// Is this administrative?
|
||||
parsed.is_admin = parsed.tags['display-name'] === 'jtv';
|
||||
if ( parsed.is_admin ) {
|
||||
parsed.style = 'admin';
|
||||
parsed.tags['display-name'] = undefined;
|
||||
}
|
||||
|
||||
// Is this a ban?
|
||||
var match = parsed.is_admin && BAN_REGEX.exec(parsed.message);
|
||||
if ( match ) {
|
||||
var unban = match[2] === 'unbanned',
|
||||
duration = unban ? -Infinity : match[2] === 'banned' ? Infinity : utils.parse_lv_duration(match[3]),
|
||||
reasons = match[4] ? match[4].trim().split(/\s*,\s*/) : [],
|
||||
ban_count = match[5] && parseInt(match[5]) || 1;
|
||||
|
||||
parsed.message = this.format_ban_notice(
|
||||
parsed.from, false, duration, ban_count, reasons,
|
||||
parsed.mod_logs && Object.keys(parsed.mod_logs),
|
||||
parsed.mod_logs);
|
||||
|
||||
parsed.cachedTokens = [{type: "raw", html: parsed.message}];
|
||||
|
||||
} else if ( parsed.from === "jtv" && parsed.mod_logs ) {
|
||||
parsed.message = Object.keys(parsed.mod_logs).join(", ") + " used: " + parsed.message;
|
||||
parsed.cachedTokens = [{type: "text", text: parsed.message}];
|
||||
}
|
||||
|
||||
if ( parsed.tags.color )
|
||||
parsed.color = parsed.tags.color;
|
||||
|
@ -109,7 +134,9 @@ FFZ.prototype.lv_parse_message = function(message) {
|
|||
parsed.tags.mod = parsed.from === parsed.room || badges.hasOwnProperty('staff') || badges.hasOwnProperty('admin') || badges.hasOwnProperty('global_mod') || badges.hasOwnProperty('moderator');
|
||||
}
|
||||
|
||||
this.tokenize_chat_line(parsed, true, room && room.get('roomProperties.hide_chat_links'));
|
||||
if ( ! parsed.cachedTokens )
|
||||
this.tokenize_chat_line(parsed, true, room && room.get('roomProperties.hide_chat_links'));
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
|
@ -670,7 +697,7 @@ FFZ.mod_card_pages.notes = {
|
|||
// We want to listen to get new notes for this user.
|
||||
mod_card._lv_sock_room = room_id;
|
||||
mod_card._lv_sock_user = user_id;
|
||||
f.lv_ws_sub(room_id + '-' + user_id);
|
||||
f.lv_ws_sub('logs-' + room_id + '-' + user_id);
|
||||
|
||||
if ( data.length ) {
|
||||
var last_line = null;
|
||||
|
|
|
@ -130,7 +130,7 @@ FFZ.prototype.clear_notifications = function() {
|
|||
|
||||
FFZ.prototype.show_notification = function(message, title, tag, timeout, on_click, on_close) {
|
||||
var perm = Notification.permission;
|
||||
if ( perm === "denied " )
|
||||
if ( perm === "denied" )
|
||||
return false;
|
||||
|
||||
if ( perm === "granted" ) {
|
||||
|
|
27
src/utils.js
27
src/utils.js
|
@ -70,6 +70,8 @@ var createElement = function(tag, className, content) {
|
|||
return num + "th";
|
||||
},
|
||||
|
||||
lv_duration_regex = / ?(\d+) ?(\w+)/g,
|
||||
|
||||
date_regex = /^(\d{4}|\+\d{6})(?:-?(\d{2})(?:-?(\d{2})(?:T(\d{2})(?::?(\d{2})(?::?(\d{2})(?:(?:\.|,)(\d{1,}))?)?)?(Z|([\-+])(\d{2})(?::?(\d{2}))?)?)?)?)?$/,
|
||||
|
||||
parse_date = function(str) {
|
||||
|
@ -873,10 +875,33 @@ module.exports = FFZ.utils = {
|
|||
minutes = Math.floor(seconds / 60);
|
||||
seconds %= 60;
|
||||
|
||||
var out = DURATIONS[val] = (weeks ? weeks + 'w' : '') + ((days || (weeks && (hours || minutes || seconds))) ? days + 'd' : '') + ((hours || ((weeks || days) && (minutes || seconds))) ? hours + 'h' : '') + ((minutes || ((weeks || days || hours) && seconds)) ? minutes + 'm' : '') + (seconds ? seconds + 's' : '');
|
||||
var out = DURATIONS[val] = (weeks ? weeks + 'w' : '') +
|
||||
(days ? days + 'd' : '') +
|
||||
(hours ? hours + 'h' : '') +
|
||||
(minutes ? minutes + 'm' : '') +
|
||||
(seconds ? seconds + 's' : '');
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
parse_lv_duration: function(input) {
|
||||
var match, value = 0;
|
||||
while(match = lv_duration_regex.exec(input)) {
|
||||
var mod = match[2],
|
||||
val = parseInt(match[1]);
|
||||
if ( mod === 'd' )
|
||||
value += val * 86400;
|
||||
else if ( mod === 'hrs' )
|
||||
value += val * 3600;
|
||||
else if ( mod === 'min' )
|
||||
value += val * 60;
|
||||
else if ( mod === 'sec' )
|
||||
value += val;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
format_unread: function(count) {
|
||||
if ( count < 1 )
|
||||
return "";
|
||||
|
|
20
style.css
20
style.css
|
@ -3611,6 +3611,26 @@ body:not(.ffz-channel-bar-bottom).ffz-small-player.ffz-minimal-channel-bar #play
|
|||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* Ban Notices */
|
||||
|
||||
.theatre .chat-messages .chat-line.admin .message,
|
||||
.dark .chat-messages .chat-line.admin .message,
|
||||
.force-dark .chat-messages .chat-line.admin .message,
|
||||
.theatre .chat-messages .chat-line.notification .message,
|
||||
.dark .chat-messages .chat-line.notification .message,
|
||||
.force-dark .chat-messages .chat-line.notification .message {
|
||||
color: #777
|
||||
}
|
||||
|
||||
.ban-tip { border-bottom: 1px dotted rgba(102,102,102,0.5); }
|
||||
|
||||
.chat-display .chat-line .ban-target { font-weight: bold }
|
||||
.chat-display .chat-line .ban-target:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* Following Menu */
|
||||
|
||||
#ffz-metadata-popup .scroller {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue