2015-01-20 01:53:18 -05:00
var FFZ = window . FrankerFaceZ ,
2015-07-29 01:03:10 -04:00
HOSTED _SUB = / subscribed to / ,
2015-01-20 01:53:18 -05:00
constants = require ( '../constants' ) ,
utils = require ( '../utils' ) ,
2016-10-27 14:32:35 -04:00
tmimotes ,
2017-09-15 14:41:00 -04:00
commerce ,
CLIP _URL = /\b(?:https?:\/\/)?clips\.twitch\.tv\/(\w+)(?:\/)?(\w+)?(?:\/edit)?\b/ ,
VIDEO _URL = /\b(?:https?:\/\/)?(?:www\.)?twitch\.tv\/(?:\w+\/v|videos)\/(\w+)(?:\?[\w\.-=]*)?\b/ ,
FFZ _URL = /\b(?:https?:\/\/)?(?:www\.)?frankerfacez\.com\/emoticon\/(\d+)(?:-\w*)?\b/ ,
2015-01-20 01:53:18 -05:00
2016-09-09 17:34:20 -04:00
NOTICE _MAPPING = {
'slow' : 'slow_on' ,
'slowoff' : 'slow_off' ,
'r9kbeta' : 'r9k_on' ,
'r9kbetaoff' : 'r9k_off' ,
'subscribers' : 'subs_on' ,
'subscribersoff' : 'subs_off' ,
'emoteonly' : 'emote_only_on' ,
2016-09-30 13:09:03 -04:00
'emoteonlyoff' : 'emote_only_off' ,
'host' : 'host_on' ,
2016-10-14 20:43:34 -04:00
'unhost' : 'host_off' ,
2017-02-26 19:11:28 -05:00
'clear' : 'clear_chat' ,
'followersoff' : 'followers_off' ,
'followers' : 'followers_on'
2016-09-09 17:34:20 -04:00
} ,
2016-05-06 02:23:12 -04:00
STATUS _BADGES = [
[ "r9k" , "r9k" , "This room is in R9K-mode." ] ,
2016-05-20 17:30:34 -04:00
[ "emote" , "emoteOnly" , "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode." ] ,
2016-05-06 02:23:12 -04:00
[ "sub" , "subsOnly" , "This room is in subscribers-only mode." ] ,
2016-12-16 14:31:23 -05:00
[ "follow" , function ( room ) {
2016-12-19 21:33:45 -05:00
return room && ! room . get ( 'isGroupRoom' ) && room . get ( 'followersOnly' ) > - 1
2016-12-16 14:31:23 -05:00
} , function ( room ) {
2016-12-19 21:33:45 -05:00
var name = room && ( room . get ( 'channel.display_name' ) || room . get ( "tmiRoom.displayName" ) || room . get ( 'isGroupRoom' ) ? room . get ( 'tmiRoom.name' ) : FFZ . get _capitalization ( room . get ( 'name' ) ) ) ,
duration = room && room . get ( 'followersOnly' ) || 0 ;
return "This room is in followers-only mode. You may only participate in chat if you " + ( duration > 0 ? " have been following " : 'follow ' ) + utils . sanitize ( name ) + ( duration > 0 ? " for at least " + utils . duration _string ( duration * 60 , true , true ) : '' ) + "." ;
2016-12-16 14:31:23 -05:00
} ] ,
2016-09-30 13:09:03 -04:00
[ "slow" , "slow" , function ( room ) { return "This room is in slow mode. You may send messages every <nobr>" + utils . number _commas ( room && room . get ( 'slow' ) || 120 ) + " seconds</nobr>." } ] ,
2016-05-06 02:23:12 -04:00
[ "ban" , "ffz_banned" , "You have been banned from talking in this room." ] ,
2016-09-30 13:09:03 -04:00
[ "delay" , function ( room ) {
return room && ( this . settings . chat _delay === - 1 ?
room . get ( 'roomProperties.chat_delay_duration' )
: room . get ( 'ffz_chat_delay' ) )
} , function ( room ) {
var is _mod = this . settings . chat _delay === - 1 ;
return "Artificial chat delay is enabled" + ( is _mod ? " for this channel" : "" ) + ". Messages are displayed after " + ( room ? ( is _mod ? room . get ( 'roomProperties.chat_delay_duration' ) : room . get ( 'ffz_chat_delay' ) / 1000 ) : 0 ) + " seconds" + ( is _mod ? " for <nobr>non-moderators</nobr>." : "." ) ;
} , null , function ( room ) {
return room && this . settings . chat _delay === - 1 && room . get ( 'isModeratorOrHigher' ) || false ;
} ] ,
[ "batch" , function ( ) { return this . settings . chat _batching !== 0 } , function ( ) { return "You have enabled chat message batching. Messages are displayed in <nobr>" + ( this . settings . chat _batching / 1000 ) + " second</nobr> increments." } ]
2016-05-06 02:23:12 -04:00
] ,
2015-07-18 21:10:27 -04:00
// StrimBagZ Support
2017-08-16 13:24:55 -04:00
is _android = navigator . userAgent . indexOf ( 'Android' ) !== - 1 ;
2015-11-07 22:56:15 -05:00
2015-01-20 01:53:18 -05:00
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _room = function ( ) {
2016-10-27 14:32:35 -04:00
try {
tmimotes = window . require && window . require ( "web-client/utilities/tmi-emotes" ) . default ;
} catch ( err ) { }
2017-09-15 14:41:00 -04:00
try {
commerce = window . require && window . require ( "web-client/utils/commerce/chat" ) ;
} catch ( err ) { }
2015-01-20 01:53:18 -05:00
this . log ( "Creating room style element." ) ;
2016-09-09 17:34:20 -04:00
var f = this ,
s = this . _room _style = document . createElement ( "style" ) ;
2015-01-20 01:53:18 -05:00
s . id = "ffz-room-css" ;
document . head . appendChild ( s ) ;
2016-09-09 17:34:20 -04:00
this . log ( "Hooking the Ember Chat PubSub service." ) ;
var PubSub = utils . ember _lookup ( 'service:chat-pubsub' ) ;
if ( PubSub )
this . _modify _chat _pubsub ( PubSub ) ;
else
this . error ( "Cannot locate the Chat PubSub service." ) ;
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room controller." ) ;
2015-01-20 01:53:18 -05:00
2015-05-17 19:02:57 -04:00
// Responsive ban button.
2016-09-09 17:34:20 -04:00
var RC = utils . ember _lookup ( 'controller:room' ) ;
2016-03-23 19:28:22 -04:00
2015-05-17 19:02:57 -04:00
if ( RC ) {
2015-07-04 17:06:36 -04:00
var orig _ban = RC . _actions . banUser ,
2016-07-13 02:06:50 -04:00
orig _to = RC . _actions . timeoutUser ,
orig _show = RC . _actions . showModOverlay ;
2015-07-04 17:06:36 -04:00
2015-05-17 19:02:57 -04:00
RC . _actions . banUser = function ( e ) {
2016-03-23 19:28:22 -04:00
orig _ban . call ( this , e ) ;
2016-05-13 23:56:59 -04:00
this . get ( "model" ) . clearMessages ( e . user , null , true ) ;
2015-07-04 17:06:36 -04:00
}
RC . _actions . timeoutUser = function ( e ) {
2016-03-23 19:28:22 -04:00
orig _to . call ( this , e ) ;
2016-05-13 23:56:59 -04:00
this . get ( "model" ) . clearMessages ( e . user , null , true ) ;
2015-05-17 19:02:57 -04:00
}
2015-12-12 13:28:35 -05:00
2016-07-13 02:06:50 -04:00
2015-12-12 13:28:35 -05:00
RC . _actions . showModOverlay = function ( e ) {
2016-07-13 02:06:50 -04:00
var Channel = utils . ember _resolve ( 'model:deprecated-channel' ) ,
chan = Channel && Channel . find && Channel . find ( { id : e . sender } ) ;
2015-12-12 13:28:35 -05:00
2016-07-13 02:06:50 -04:00
if ( ! chan ) {
f . log ( "Error opening mod card. model:deprecated-channel does not exist or does not have find!" ) ;
return orig _show . call ( this , e ) ;
}
2015-12-12 13:28:35 -05:00
// Don't try loading the channel if it's already loaded. Don't make mod cards
// refresh the channel page when you click the broadcaster, basically.
if ( ! chan . get ( 'isLoaded' ) )
chan . load ( ) ;
this . set ( "showModerationCard" , true ) ;
// We pass in renderBottom and renderRight, which we use to reposition the window
2016-06-22 14:23:03 -04:00
// after we know how big it actually is. This doesn't work a lot of the time.
2015-12-12 13:28:35 -05:00
this . set ( "moderationCardInfo" , {
user : chan ,
2016-06-22 14:23:03 -04:00
renderTop : e . real _top || e . top ,
2015-12-12 13:28:35 -05:00
renderLeft : e . left ,
renderBottom : e . bottom ,
renderRight : e . right ,
isIgnored : this . get ( "tmiSession" ) . isIgnored ( e . sender ) ,
2016-03-23 19:28:22 -04:00
isChannelOwner : this . get ( "login.userData.login" ) === e . sender ,
2016-12-14 02:40:18 -05:00
profileHref : Twitch . uri . channel ( e . sender ) ,
2015-12-12 13:28:35 -05:00
isModeratorOrHigher : this . get ( "model.isModeratorOrHigher" )
} ) ;
}
2015-05-17 19:02:57 -04:00
}
2015-07-04 17:06:36 -04:00
this . log ( "Hooking the Ember Room model." ) ;
2016-03-23 20:23:04 -04:00
var Room = utils . ember _resolve ( 'model:room' ) ;
2015-01-20 01:53:18 -05:00
this . _modify _room ( Room ) ;
// Modify all current instances of Room, as the changes to the base
// class won't be inherited automatically.
var instances = Room . instances ;
for ( var key in instances ) {
if ( ! instances . hasOwnProperty ( key ) )
continue ;
var inst = instances [ key ] ;
this . add _room ( inst . id , inst ) ;
this . _modify _room ( inst ) ;
2016-10-16 15:05:11 -04:00
inst . ffzUpdateBadges ( ) ;
2015-06-05 03:59:28 -04:00
inst . ffzPatchTMI ( ) ;
2015-01-20 01:53:18 -05:00
}
2015-07-04 17:06:36 -04:00
2016-10-25 14:44:22 -04:00
2017-06-11 13:30:37 -04:00
var ChannelSubs = utils . ember _lookup ( 'service:channel-subscriptions' ) ;
if ( ChannelSubs ) {
this . log ( "Hooking the Ember Channel Subscriptions service." ) ;
ChannelSubs . reopen ( {
populateSubNotificationTokens : function ( ) {
var Chat = utils . ember _lookup ( 'controller:chat' ) ,
tokens = Chat && Chat . get ( 'currentRoom.roomProperties.available_chat_notification_tokens' ) ;
return this . _super ( tokens ) ;
} ,
_reloadSubNotificationToken : function ( e ) {
var t = this ;
return this . get ( "api" ) . request ( "get" , "/api/channels/" + e + "/chat_properties" ) . then ( function ( e ) {
var room = f . rooms && f . rooms [ e ] && f . rooms [ e ] . room ;
room && room . set ( 'roomProperties.available_chat_notification_tokens' , e . available _chat _notification _tokens ) ;
t . isDestroyed || t . populateSubNotificationTokens ( e . available _chat _notification _tokens )
} )
} ,
} ) ;
}
2016-10-25 14:44:22 -04:00
this . update _views ( 'component:chat/chat-room' , this . modify _room _component ) ;
this . update _views ( 'component:chat/chat-interface' , this . modify _chat _interface ) ;
//this.log("Hooking the Ember Room view.");
//this.update_views('view:room', this.modify_room_view);
2015-07-04 17:06:36 -04:00
}
2016-10-14 20:43:34 -04:00
// --------------------
// 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 ] ;
2016-10-18 00:35:31 -04:00
var nd = ( notice [ 0 ] === - Infinity || notice [ 0 ] === - 1 ) ? 'unban' : ( notice [ 0 ] !== null && isFinite ( notice [ 0 ] ) ) ? utils . duration _string ( notice [ 0 ] , true ) : 'ban' ;
2016-10-14 20:43:34 -04:00
duration _tip . push ( utils . sanitize ( mod _id ) + ' - ' + nd + ( notice [ 1 ] ? ': ' + utils . sanitize ( notice [ 1 ] ) : '' ) ) ;
}
2016-11-28 17:00:27 -05:00
return ( is _me ? 'You have' : '<span data-user="' + utils . quote _san ( username ) + '" class="user-token html-tooltip" title="' + utils . quote _attr ( name [ 1 ] || '' ) + '">' + name [ 0 ] + '</span> has' ) +
2016-10-14 20:43:34 -04:00
' been ' + ( duration _tip . length ? '<span class="ban-tip html-tooltip" title="' + utils . quote _attr ( duration _tip . join ( '<br>' ) ) + '">' : '' ) + ( duration === - Infinity ? 'unbanned' :
2016-10-27 12:49:31 -04:00
( duration === - 1 ? 'untimed out' :
( duration === 1 ? 'purged' : isFinite ( duration ) ? 'timed out for ' + utils . duration _string ( duration ) : 'banned' ) ) ) +
2016-10-14 20:43:34 -04:00
( 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 ( ', ' ) ) : '.' ) ;
}
2016-09-09 17:34:20 -04:00
// --------------------
// PubSub is fucking awful
// --------------------
FFZ . prototype . _modify _chat _pubsub = function ( pubsub ) {
var f = this ;
pubsub . reopen ( {
setupService : function ( room _id , t ) {
var n = this ;
this . get ( "session" ) . withCurrentUser ( function ( user ) {
if ( n . isDestroyed )
return ;
var ps = n . _pubsub ( ) ,
token = user . chat _oauth _token ,
new _topics = [
"chat_message_updated." + room _id ,
2016-09-30 13:09:03 -04:00
"chat_moderator_actions." + user . id + "." + room _id ] ;
2016-09-09 17:34:20 -04:00
for ( var i = 0 ; i < new _topics . length ; i ++ )
ps . Listen ( {
topic : new _topics [ i ] ,
auth : token ,
success : function ( ) { } ,
2016-09-30 13:09:03 -04:00
failure : function ( t ) { f . log ( "[PubSub] Failed to listen to topic: " + new _topics [ i ] , t ) ; } ,
2016-09-09 17:34:20 -04:00
message : Ember . run . bind ( n , n . _onPubsubMessage , new _topics [ i ] )
} ) ;
if ( n . chatTopics )
n . chatTopics = n . chatTopics . concat ( new _topics ) ;
else
n . chatTopics = new _topics ;
ps . on ( "connected" , Ember . run . bind ( n , n . _onPubsubConnect ) ) ;
ps . on ( "disconnected" , Ember . run . bind ( n , n . _onPubsubDisconnect ) ) ;
t ( ) ;
} ) ;
} ,
tearDownService : function ( room _id ) {
if ( ! this . chatTopics )
return ;
var ps = this . _pubsub ( ) ,
old _topics ;
if ( ! room _id )
room _id = this . get ( "ffz_teardown_target" ) ;
if ( room _id ) {
// Make sure it's a string.
room _id = '.' + room _id ;
old _topics = this . chatTopics . filter ( function ( x ) { return x . substr ( - room _id . length ) === room _id } ) ;
} else
old _topics = this . chatTopics ;
for ( var i = 0 ; i < old _topics . length ; i ++ ) {
2016-10-11 21:41:35 -04:00
var topic = old _topics [ 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 ( ) { }
} ) ;
2016-09-09 17:34:20 -04:00
ps . Unlisten ( {
2016-10-11 21:41:35 -04:00
topic : topic ,
2016-09-09 17:34:20 -04:00
success : function ( ) { } ,
2016-10-11 21:41:35 -04:00
failure : function ( topic , t ) { f . log ( "[PubSub] Failed to unlisten to topic: " + topic , t ) ; } . bind ( this , topic )
2016-09-09 17:34:20 -04:00
} ) ;
this . chatTopics . removeObject ( old _topics [ i ] ) ;
}
if ( ! this . chatTopics . length )
this . chatTopics = null ;
} ,
_onPubsubMessage : function ( topic , e ) {
if ( this . isDestroyed )
return ;
var msg = JSON . parse ( e ) ,
msg _data = msg . data ,
msg _type = msg . type || msg _data . type ;
if ( msg _data )
msg _data . topic = topic ;
this . trigger ( msg _type , msg _data ) ;
}
} ) ;
if ( ! pubsub . chatTopics )
return ;
// Now that we've modified that, we need to re-listen to everything.
pubsub . get ( "session" ) . withCurrentUser ( function ( user ) {
if ( pubsub . isDestroyed )
return ;
var ps = pubsub . _pubsub ( ) ,
2016-10-14 20:43:34 -04:00
token = user . chat _oauth _token ,
internal _topics = ps . _client & ps . _client . _listens && ps . _client . _listens . _events || { } ;
2016-09-09 17:34:20 -04:00
for ( var i = 0 ; i < pubsub . chatTopics . length ; i ++ ) {
2016-10-11 21:41:35 -04:00
var topic = pubsub . chatTopics [ i ] ;
2016-09-09 17:34:20 -04:00
ps . Unlisten ( {
2016-10-11 21:41:35 -04:00
topic : topic ,
2016-09-09 17:34:20 -04:00
success : function ( ) { } ,
2016-10-11 21:41:35 -04:00
failure : function ( topic , t ) { f . log ( "[PubSub] Failed to unlisten to topic: " + topic , t ) ; } . bind ( this , topic )
2016-09-09 17:34:20 -04:00
} ) ;
2016-10-14 20:43:34 -04:00
// 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.
2016-09-09 17:34:20 -04:00
ps . Listen ( {
2016-10-11 21:41:35 -04:00
topic : topic ,
2016-09-09 17:34:20 -04:00
auth : token ,
success : function ( ) { } ,
2016-10-11 21:41:35 -04:00
failure : function ( topic , t ) { f . log ( "[PubSub] Failed to listen to topic: " + topic , t ) ; } . bind ( this , topic ) ,
message : Ember . run . bind ( pubsub , pubsub . _onPubsubMessage , topic )
2016-09-09 17:34:20 -04:00
} ) ;
}
} ) ;
}
2015-07-04 17:06:36 -04:00
// --------------------
// View Customization
// --------------------
2016-10-25 14:44:22 -04:00
FFZ . prototype . modify _chat _interface = function ( component ) {
var f = this ;
utils . ember _reopen _view ( component , {
ffz _init : function ( ) {
f . _chati = this ;
} ,
ffz _destroy : function ( ) {
if ( f . _chati === this )
f . _chati = undefined ;
} ,
submitButtonText : function ( ) {
if ( this . get ( 'room.isWhisperMessage' ) )
return i18n ( 'Whisper' ) ;
var wait = this . get ( 'room.slowWait' ) ,
msg = this . get ( 'room.messageToSend' ) || '' ,
first _char = msg . charAt ( 0 ) ,
is _cmd = first _char === '/' || first _char === '.' ;
if ( ( is _cmd && msg . substr ( 0 , 4 ) !== '/me ' ) || ! wait || ! f . settings . room _status )
return i18n ( 'Chat' ) ;
return utils . time _to _string ( wait , false , false , true ) ;
} . property ( 'room.isWhisperMessage' , 'room.slowWait' )
} ) ;
}
FFZ . HoverPause = {
ffz _frozen : false ,
ffz _ctrl : false ,
ffz _freeze _selector : '.chat-messages' ,
ffzAddKeyHook : function ( ) {
if ( ! this . _ffz _freeze _key ) {
var key = this . _ffz _freeze _key = this . ffzFreezeOnKey . bind ( this ) ;
document . body . addEventListener ( 'keydown' , key ) ;
document . body . addEventListener ( 'keyup' , key ) ;
}
} ,
ffzRemoveKeyHook : function ( ) {
if ( this . _ffz _freeze _key ) {
var key = this . _ffz _freeze _key ;
this . _ffz _freeze _key = null ;
document . body . removeEventListener ( 'keydown' , key ) ;
document . body . removeEventListener ( 'keyup' , key ) ;
}
} ,
ffzEnableFreeze : function ( ) {
var el = this . get ( 'element' ) ,
2016-10-27 12:49:31 -04:00
scroller = this . get ( 'chatMessagesScroller' ) ,
messages = ( scroller && scroller [ 0 ] ) || ( el && el . querySelector ( this . get ( 'ffz_freeze_selector' ) ) ) ;
2016-10-25 14:44:22 -04:00
if ( ! messages )
return ;
this . _ffz _freeze _messages = messages ;
if ( ! this . _ffz _freeze _mouse _move ) {
var mm = this . _ffz _freeze _mouse _move = this . ffzFreezeMouseMove . bind ( this ) ;
messages . addEventListener ( 'mousemove' , mm ) ;
messages . addEventListener ( 'touchmove' , mm ) ;
}
if ( ! this . _ffz _freeze _mouse _out ) {
var mo = this . _ffz _freeze _mouse _out = this . ffzFreezeMouseOut . bind ( this ) ;
messages . addEventListener ( 'mouseout' , mo ) ;
}
this . ffzAddKeyHook ( ) ;
} ,
ffzDisableFreeze : function ( ) {
if ( this . _ffz _freeze _interval ) {
clearInterval ( this . _ffz _freeze _interval ) ;
this . _ffz _freeze _interval = null ;
}
if ( this . _ffz _freeze _messages ) {
var messages = this . _ffz _freeze _messages ;
this . _ffz _freeze _messages = null ;
if ( this . _ffz _freeze _mouse _move ) {
var mm = this . _ffz _freeze _mouse _move ;
this . _ffz _freeze _mouse _move = null ;
messages . removeEventListener ( 'mousemove' , mm ) ;
messages . removeEventListener ( 'touchmove' , mm ) ;
}
if ( this . _ffz _freeze _mouse _out ) {
messages . removeEventListener ( 'mouseout' , this . _ffz _freeze _mouse _out ) ;
this . _ffz _freeze _mouse _out = null ;
}
}
} ,
ffzFreezePulse : function ( ) {
if ( this . ffz _frozen && ! this . ffzShouldBeFrozen ( ) )
this . ffzUnfreeze ( ) ;
} ,
ffzFreeze : function ( ) {
this . set ( 'ffz_frozen' , true ) ;
if ( this . get ( 'stuckToBottom' ) ) {
if ( ! this . _ffz _freeze _interval )
this . _ffz _freeze _interval = setInterval ( this . ffzFreezePulse . bind ( this ) , 200 ) ;
2016-10-25 16:26:57 -04:00
this . ffzFreezeUpdateBuffer && this . ffzFreezeUpdateBuffer ( false ) ;
2016-10-25 14:44:22 -04:00
this . ffzFreezeWarn ( ) ;
}
} ,
ffzUnfreeze : function ( from _stuck ) {
this . set ( 'ffz_frozen' , false ) ;
this . _ffz _freeze _last _move = 0 ;
this . ffzFreezeUnwarn ( ) ;
if ( this . _ffz _freeze _interval ) {
clearInterval ( this . _ffz _freeze _interval ) ;
this . _ffz _freeze _interval = null ;
}
if ( ! from _stuck && this . get ( 'stuckToBottom' ) )
this . _scrollToBottom ( ) ;
} ,
ffzFreezeWarn : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( ! el )
return ;
if ( warning )
return warning . classList . remove ( 'hidden' ) ;
warning = utils . createElement ( 'div' , 'more-messages-indicator ffz-freeze-indicator' ) ;
var hp = FFZ . get ( ) . settings . chat _hover _pause ,
label = hp === 2 ? 'Ctrl Key' :
hp === 3 ? ( constants . META _NAME + ' Key' ) :
hp === 4 ? 'Alt Key' :
hp === 5 ? 'Shift Key' :
hp === 6 ? 'Ctrl or Mouse' :
hp === 7 ? ( constants . META _NAME + ' or Mouse' ) :
hp === 8 ? 'Alt or Mouse' :
hp === 9 ? 'Shift or Mouse' :
'Mouse Movement' ;
warning . innerHTML = '(Chat Paused Due to ' + label + ')' ;
var cont = el . querySelector ( '.chat-interface' ) ;
if ( ! cont )
return ;
cont . insertBefore ( warning , cont . childNodes [ 0 ] )
} ,
ffzFreezeUnwarn : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( warning )
warning . classList . add ( 'hidden' ) ;
} ,
ffzShouldBeFrozen : function ( since ) {
if ( since === undefined )
since = Date . now ( ) - this . _ffz _freeze _last _move ;
var hp = FFZ . get ( ) . settings . chat _hover _pause ;
return ( this . _ffz _freeze _ctrl && ( hp === 2 || hp === 6 ) ) ||
( this . _ffz _freeze _meta && ( hp === 3 || hp === 7 ) ) ||
( this . _ffz _freeze _alt && ( hp === 4 || hp === 8 ) ) ||
( this . _ffz _freeze _shift && ( hp === 5 || hp === 9 ) ) ||
( since < 750 && ( hp === 1 || hp > 5 ) ) ;
} ,
ffzFreezeMouseOut : function ( event ) {
this . _ffz _freeze _outside = true ;
var e = this ;
setTimeout ( function ( ) {
if ( e . _ffz _freeze _outside ) {
if ( FFZ . get ( ) . settings . chat _mod _icon _visibility > 1 )
e . get ( 'element' ) . classList . toggle ( 'show-mod-icons' , false ) ;
e . ffzUnfreeze ( ) ;
}
} , 25 ) ;
} ,
ffzFreezeOnKey : function ( event ) {
this . _ffz _freeze _alt = event . altKey ;
this . _ffz _freeze _ctrl = event . ctrlKey ;
this . _ffz _freeze _meta = event . metaKey ;
this . _ffz _freeze _shift = event . shiftKey ;
var f = FFZ . get ( ) ,
cmi = f . settings . chat _mod _icon _visibility ;
if ( ! this . _ffz _freeze _outside && cmi > 1 )
this . get ( 'element' ) . classList . toggle ( 'show-mod-icons' ,
cmi === 2 ? this . _ffz _freeze _ctrl :
2016-10-27 12:49:31 -04:00
cmi === 3 ? this . _ffz _freeze _meta :
2016-10-25 14:44:22 -04:00
cmi === 4 ? this . _ffz _freeze _alt :
this . _ffz _freeze _shift ) ;
2016-10-27 12:49:31 -04:00
if ( this . _ffz _freeze _outside || f . settings . chat _hover _pause < 2 )
2016-10-25 14:44:22 -04:00
return ;
// Okay, so at this point we should change the state of the freeze?
var should _freeze = this . ffzShouldBeFrozen ( ) ,
freeze _change = this . ffz _frozen !== should _freeze ;
if ( freeze _change )
if ( should _freeze )
this . ffzFreeze ( ) ;
else
this . ffzUnfreeze ( ) ;
} ,
ffzFreezeMouseMove : function ( event ) {
this . _ffz _freeze _last _move = Date . now ( ) ;
this . _ffz _freeze _outside = false ;
// If nothing of interest has happened, stop.
if ( event . altKey === this . _ffz _freeze _alt &&
event . shiftKey === this . _ffz _freeze _shift &&
event . ctrlKey === this . _ffz _freeze _ctrl &&
event . metaKey === this . _ffz _freeze _meta &&
event . screenX === this . _ffz _freeze _last _screenx &&
event . screenY === this . _ffz _freeze _last _screeny )
return ;
// Grab state.
this . _ffz _freeze _alt = event . altKey ;
this . _ffz _freeze _ctrl = event . ctrlKey ;
this . _ffz _freeze _meta = event . metaKey ;
this . _ffz _freeze _shift = event . shiftKey ;
this . _ffz _freeze _last _screenx = event . screenX ;
this . _ffz _freeze _last _screeny = event . screenY ;
var cmi = FFZ . get ( ) . settings . chat _mod _icon _visibility ;
if ( ! this . _ffz _freeze _outside && cmi > 1 )
this . get ( 'element' ) . classList . toggle ( 'show-mod-icons' ,
cmi === 2 ? this . _ffz _freeze _ctrl :
2016-10-27 12:49:31 -04:00
cmi === 3 ? this . _ffz _freeze _meta :
2016-10-25 14:44:22 -04:00
cmi === 4 ? this . _ffz _freeze _alt :
this . _ffz _freeze _shift ) ;
var should _freeze = this . ffzShouldBeFrozen ( ) ,
freeze _change = this . ffz _frozen !== should _freeze ;
if ( freeze _change )
if ( should _freeze )
this . ffzFreeze ( ) ;
else
this . ffzUnfreeze ( ) ;
} ,
_prepareStickyBottom : function ( ) {
2016-10-27 12:49:31 -04:00
var t = this ,
scroller = this . get ( 'chatMessagesScroller' ) || this . _$chatMessagesScroller ;
2016-10-25 14:44:22 -04:00
this . _setStuckToBottom ( true ) ;
2016-10-27 12:49:31 -04:00
scroller . on ( this . _scrollEvents , function ( e ) {
if ( scroller && scroller [ 0 ] && ( ( ! t . ffz _frozen && 'mousedown' === e . type ) || 'wheel' === e . type || 'mousewheel' === e . type ) ) {
var distance = scroller [ 0 ] . scrollHeight - scroller [ 0 ] . scrollTop - scroller [ 0 ] . offsetHeight ;
2016-10-25 16:26:57 -04:00
t . _setStuckToBottom ( distance <= 10 ) ;
2016-10-25 14:44:22 -04:00
}
} ) ;
} ,
ffzFixStickyBottom : function ( ) {
2016-10-27 12:49:31 -04:00
( this . get ( 'chatMessagesScroller' ) || this . _$chatMessagesScroller ) . off ( this . _scrollEvents ) ;
2016-10-25 14:44:22 -04:00
this . _prepareStickyBottom ( ) ;
} ,
_scrollToBottom : function ( ) {
var e = this ;
this . _scrollToBottomRequested = true ;
Ember . run . schedule ( "afterRender" , function ( ) {
if ( ! e . ffz _frozen && ! e . isDestroyed && ! e . isDestroying && e . _scrollToBottomRequested ) {
2016-10-27 12:49:31 -04:00
var t = e . get ( 'chatMessagesScroller' ) || e . _$chatMessagesScroller ;
2016-10-25 14:44:22 -04:00
if ( t && t . length ) {
t [ 0 ] . scrollTop = t [ 0 ] . scrollHeight ;
e . _setStuckToBottom ( true ) ;
}
}
} ) ;
2016-10-25 16:26:57 -04:00
} ,
_setStuckToBottom : function ( val ) {
this . set ( 'stuckToBottom' , val ) ;
this . ffzFreezeUpdateBuffer && this . ffzFreezeUpdateBuffer ( val ) ;
if ( ! val )
this . ffzUnfreeze ( ) ;
2016-10-25 14:44:22 -04:00
}
}
FFZ . prototype . modify _room _component = function ( component ) {
2016-11-09 22:19:37 -05:00
var f = this ,
2017-06-11 13:30:37 -04:00
PinnedCheers = utils . ember _lookup ( 'service:bits-pinned-cheers' ) ,
ChannelSubs = utils . ember _lookup ( 'service:channel-subscriptions' ) ;
2016-11-09 22:19:37 -05:00
2016-10-25 14:44:22 -04:00
utils . ember _reopen _view ( component , _ . extend ( {
ffz _init : function ( ) {
f . _roomv = this ;
2017-04-10 15:03:05 -04:00
if ( ! f . has _bttv _6 ) {
2016-10-27 12:49:31 -04:00
this . ffzFixStickyBottom ( ) ;
this . ffzAddKeyHook ( ) ;
2016-10-25 14:44:22 -04:00
2016-10-27 12:49:31 -04:00
if ( f . settings . chat _hover _pause )
this . ffzEnableFreeze ( ) ;
2017-04-10 15:03:05 -04:00
}
2016-10-25 14:44:22 -04:00
2017-04-10 15:03:05 -04:00
if ( ! f . has _bttv ) {
2016-10-27 12:49:31 -04:00
if ( f . settings . room _status )
this . ffzUpdateStatus ( ) ;
}
2017-06-11 13:30:37 -04:00
var t = this ,
actions = this . _actions || { } ,
2016-10-27 12:49:31 -04:00
orig _show = actions . showModOverlay ;
2017-06-11 13:30:37 -04:00
actions . accommodatePinnedMessage = function ( e ) {
var el = t . get ( 'element' ) ,
chat = el . querySelector ( '.js-chat-messages' ) ;
if ( chat )
chat . dataset . pinned _height = e ;
} ;
2016-10-27 12:49:31 -04:00
actions . showModOverlay = function ( e ) {
var Channel = utils . ember _resolve ( 'model:deprecated-channel' ) ,
chan = Channel && Channel . find && Channel . find ( { id : e . sender } ) ;
if ( ! chan ) {
f . log ( "Error opening mod card. model:deprecated-channel does not exist or does not have find!" ) ;
return orig _show && orig _show . call ( this , e ) ;
}
2016-10-25 14:44:22 -04:00
2016-10-27 12:49:31 -04:00
// Don't try loading the channel if it's already loaded. Don't make mod cards
// refresh the channel page when you click the broadcaster, basically.
if ( ! chan . get ( 'isLoaded' ) )
chan . load ( ) ;
this . set ( "showModerationCard" , true ) ;
// We pass in renderBottom and renderRight, which we use to reposition the window
// after we know how big it actually is. This doesn't work a lot of the time.
this . set ( "moderationCardInfo" , {
user : chan ,
renderTop : e . real _top || e . top ,
renderLeft : e . left ,
renderBottom : e . bottom ,
renderRight : e . right ,
isIgnored : this . get ( "tmiSession" ) . isIgnored ( e . sender ) ,
isChannelOwner : this . get ( "session.userData.login" ) === e . sender ,
profileHref : Twitch . uri . profile ( e . sender ) ,
isModeratorOrHigher : this . get ( "room.isModeratorOrHigher" )
} ) ;
}
2017-06-11 13:30:37 -04:00
this . ffzUpdateRecent ( ) ;
2016-10-25 14:44:22 -04:00
} ,
ffz _destroy : function ( ) {
if ( f . _roomv === this )
f . _roomv = undefined ;
this . ffzDisableFreeze ( ) ;
this . ffzRemoveKeyHook ( ) ;
} ,
2017-06-11 13:30:37 -04:00
ffzUpdateRecent : function ( ) {
var t = this ,
el = this . get ( 'element' ) ,
container = this . get ( 'ffz_recent_el' ) ,
con _count = this . get ( 'ffz_recent_count_el' ) ,
should _show = f . settings . recent _highlights && ! f . has _bttv ;
if ( ! el )
return ;
if ( ! container ) {
if ( ! should _show )
return ;
container = utils . createElement ( 'ul' , 'chat-history' ) ;
var expander = utils . createElement ( 'div' , 'ffz-recent-expando' , 'Recent Highlights<span class="pill"></span>' ) ,
big _el = utils . createElement ( 'div' , 'ffz-recent-messages' , expander ) ,
btn _handle = utils . createElement ( 'span' , 'ffz-handle ffz-close-button' ) ,
super _parent = document . body . querySelector ( '.app-main' ) ;
con _count = expander . querySelector ( '.pill' ) ;
container . classList . toggle ( 'dark' , f . settings . dark _twitch ) ;
expander . insertBefore ( btn _handle , expander . firstChild ) ;
container . dataset . docked = true ;
big _el . appendChild ( container ) ;
el . appendChild ( big _el ) ;
el . classList . add ( 'ffz-has-recent-messages' ) ;
this . set ( 'ffz_recent_el' , container ) ;
this . set ( 'ffz_recent_count_el' , con _count ) ;
expander . addEventListener ( 'mousemove' , function ( e ) {
con _count . textContent = '' ;
t . set ( 'room.ffz_recent_highlights_unread' , 0 ) ;
} ) ;
btn _handle . addEventListener ( 'click' , function ( e ) {
if ( ! big _el . classList . contains ( 'ui-moved' ) || ! e . button === 0 )
return ;
big _el . style . top = 0 ;
big _el . style . left = 0 ;
big _el . classList . remove ( 'ui-moved' ) ;
big _el . parentElement . removeChild ( big _el ) ;
el . appendChild ( big _el ) ;
el . classList . add ( 'ffz-has-recent-messages' ) ;
container . dataset . docked = true ;
} ) ;
var st ;
jQuery ( big _el ) . draggable ( {
handle : expander ,
start : function ( e ) {
st = container . scrollTop ;
big _el . classList . add ( 'ui-moved' ) ;
container . dataset . docked = false ;
el . classList . remove ( 'ffz-has-recent-messages' ) ;
} ,
stop : function ( e ) {
if ( big _el . parentElement !== super _parent ) {
var rect = big _el . getBoundingClientRect ( ) ,
pr = super _parent . getBoundingClientRect ( ) ;
big _el . parentElement . removeChild ( big _el ) ;
super _parent . appendChild ( big _el ) ;
big _el . style . top = ( rect . top - pr . top ) + "px" ;
big _el . style . left = ( rect . left - pr . left ) + "px" ;
}
container . scrollTop = st ;
}
} ) ;
jQuery ( container ) . on ( 'click' , '.chat-line' , function ( e ) {
if ( ! e . target || e . button !== 0 )
return ;
var jq = jQuery ( e . target ) ,
cl = e . target . classList ,
line = cl . contains ( 'chat-line' ) ? e . target : jq . parents ( '.chat-line' ) [ 0 ] ,
room _id = line && line . dataset . room ,
room = room _id && f . rooms [ room _id ] && f . rooms [ room _id ] . room ,
from = line && line . dataset . sender ,
msg _id = line && line . dataset . id ;
if ( cl . contains ( 'deleted-word' ) ) {
jq . trigger ( 'mouseout' ) ;
e . target . outerHTML = e . target . dataset . text ;
} else if ( cl . contains ( 'deleted-link' ) )
return f . _deleted _link _click . call ( e . target , e ) ;
else if ( cl . contains ( 'badge' ) ) {
if ( cl . contains ( 'click_action' ) ) {
var badge = f . badges && f . badges [ e . target . getAttribute ( 'data-badge-id' ) ] ;
if ( badge . click _action )
badge . click _action . call ( f , this . get ( 'msgObject' ) , e ) ;
} else if ( cl . contains ( 'click_url' ) )
window . open ( e . target . dataset . url , "_blank" ) ;
else if ( cl . contains ( 'turbo' ) )
window . open ( "/products/turbo?ref=chat_badge" , "_blank" ) ;
else if ( cl . contains ( 'subscriber' ) )
window . open ( "/" + room _id + "/subscribe?ref=in_chat_subscriber_link" ) ;
} else if ( f . _click _emote ( e . target , e ) )
return ;
else if ( ( f . settings . clickable _mentions && cl . contains ( 'user-token' ) ) || cl . contains ( 'from' ) || e . target . parentElement . classList . contains ( 'from' ) ) {
var target = cl . contains ( 'user-token' ) ? e . target . dataset . user : from ;
if ( ! target )
return ;
var bounds = line && line . getBoundingClientRect ( ) || document . body . getBoundingClientRect ( ) ,
x = 0 , right ;
if ( bounds . left > 400 )
right = bounds . left - 40 ;
f . _roomv . actions . showModOverlay . call ( f . _roomv , {
left : bounds . left ,
right : right ,
top : bounds . top + bounds . height ,
real _top : bounds . top ,
sender : target
} ) ;
}
} ) ;
} else if ( ! should _show ) {
jQuery ( container . parentElement ) . remove ( ) ;
this . set ( 'ffz_recent_el' , null ) ;
this . set ( 'ffz_recent_count_el' , null ) ;
el . classList . remove ( 'ffz-has-recent-messages' ) ;
return ;
}
var was _at _bottom = container . scrollTop >= ( container . scrollHeight - container . clientHeight ) ;
container . innerHTML = '' ;
con _count . textContent = container . dataset . docked ? utils . format _unread ( this . get ( 'room.ffz_recent_highlights_unread' ) || 0 ) : '' ;
var messages = this . get ( 'room.ffz_recent_highlights' ) || [ ] ;
for ( var i = 0 ; i < messages . length ; i ++ )
container . appendChild ( f . _build _mod _card _history ( messages [ i ] , null , true ) ) ;
if ( was _at _bottom )
container . scrollTop = container . scrollHeight ;
} . observes ( 'room' ) ,
2016-10-27 12:49:31 -04:00
ffzUpdateBits : function ( ) {
var t = this ,
2017-06-11 13:30:37 -04:00
channel = this . get ( 'room.channel' ) ,
bits _room = this . get ( 'bitsRoom' ) ;
if ( ! channel || ! bits _room )
2016-10-27 12:49:31 -04:00
return ;
2016-11-09 22:19:37 -05:00
PinnedCheers && PinnedCheers . dismissLocalMessage ( ) ;
if ( ! channel . get ( 'isLoaded' ) )
2017-06-11 13:30:37 -04:00
channel . load ( ) . then ( function ( ) { bits _room . reset ( ) ; t . _initializeBits ( channel ) } )
else {
bits _room . reset ( ) ;
2016-10-27 12:49:31 -04:00
this . _initializeBits ( channel ) ;
2017-06-11 13:30:37 -04:00
}
ChannelSubs && ChannelSubs . populateSubNotificationTokens ( this . get ( 'room.roomProperties.available_chat_notification_tokens' ) ) ;
2016-10-27 12:49:31 -04:00
} . observes ( 'room' ) ,
2016-10-25 16:26:57 -04:00
ffzFreezeUpdateBuffer : function ( val ) {
var room = this . get ( 'room' ) ;
if ( val === undefined )
val = this . get ( 'stuckToBottom' ) ;
if ( room )
room . messageBufferSize = f . settings . scrollback _length + ( val ? 0 : 150 ) ;
} ,
2016-10-25 14:44:22 -04:00
ffzUpdateStatus : function ( ) {
var room = this . get ( 'room' ) ,
el = this . get ( 'element' ) ,
container = el && el . querySelector ( '.chat-buttons-container' ) ;
if ( ! container )
return ;
var btn = container . querySelector ( 'button' ) ;
if ( f . has _bttv || ! f . settings . room _status ) {
jQuery ( ".ffz.room-state" , container ) . remove ( ) ;
if ( btn ) {
btn . classList . remove ( 'ffz-waiting' ) ;
btn . classList . remove ( 'ffz-banned' ) ;
}
return ;
} else if ( btn ) {
btn . classList . toggle ( 'ffz-waiting' , room && room . get ( 'slowWait' ) || 0 ) ;
btn . classList . toggle ( 'ffz-banned' , room && room . get ( 'ffz_banned' ) || false ) ;
}
var badge , id , info , vis _count = 0 , label ;
for ( var i = 0 ; i < STATUS _BADGES . length ; i ++ ) {
info = STATUS _BADGES [ i ] ;
id = 'ffz-stat-' + info [ 0 ] ;
badge = container . querySelector ( '#' + id ) ;
visible = typeof info [ 1 ] === 'function' ? info [ 1 ] . call ( f , room ) : room && room . get ( info [ 1 ] ) ;
if ( typeof visible === 'string' )
visible = visible === '1' ;
label = typeof info [ 3 ] === "function" ? info [ 3 ] . call ( f , room ) : undefined ;
if ( ! badge ) {
badge = utils . createElement ( 'span' , 'ffz room-state stat float-right' , ( label || info [ 0 ] ) . charAt ( 0 ) . toUpperCase ( ) + '<span>' + ( label || info [ 0 ] ) . substr ( 1 ) . toUpperCase ( ) + '</span>' ) ;
badge . id = id ;
jQuery ( badge ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'se' ) } ) ;
container . appendChild ( badge ) ;
}
if ( label )
badge . innerHTML = ( label || info [ 0 ] ) . charAt ( 0 ) . toUpperCase ( ) + '<span>' + ( label || info [ 0 ] ) . substr ( 1 ) . toUpperCase ( ) + '</span>' ;
badge . title = typeof info [ 2 ] === "function" ? info [ 2 ] . call ( f , room ) : info [ 2 ] ;
badge . classList . toggle ( 'hidden' , ! visible ) ;
badge . classList . toggle ( 'faded' , info [ 4 ] !== undefined ? typeof info [ 4 ] === "function" ? info [ 4 ] . call ( f , room ) : info [ 4 ] : false ) ;
if ( visible )
vis _count ++ ;
}
jQuery ( ".ffz.room-state" , container ) . toggleClass ( 'truncated' , vis _count > 3 ) ;
} . observes ( 'room' )
} , FFZ . HoverPause ) ) ;
}
2015-01-20 01:53:18 -05:00
// --------------------
// Command System
// --------------------
FFZ . chat _commands = { } ;
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands = { } ;
2015-01-20 01:53:18 -05:00
FFZ . prototype . room _message = function ( room , text ) {
2017-04-10 15:03:05 -04:00
if ( this . has _bttv _6 ) {
2016-09-30 13:09:03 -04:00
var lines = text . split ( "\n" ) ;
2015-01-20 01:53:18 -05:00
for ( var i = 0 ; i < lines . length ; i ++ )
BetterTTV . chat . handlers . onPrivmsg ( room . id , { style : 'admin' , date : new Date ( ) , from : 'jtv' , message : lines [ i ] } ) ;
2016-09-30 13:09:03 -04:00
} else
room . room . addMessage ( { ffz _line _returns : true , style : 'ffz admin' , date : new Date ( ) , from : 'FFZ' , message : text } ) ;
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . run _command = function ( text , room _id ) {
2015-02-10 01:34:23 -05:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return false ;
if ( ! text )
return ;
var args = text . split ( " " ) ,
cmd = args . shift ( ) . substr ( 1 ) . toLowerCase ( ) ,
command = FFZ . chat _commands [ cmd ] ,
output ;
if ( ! command )
return false ;
if ( command . hasOwnProperty ( 'enabled' ) ) {
var val = command . enabled ;
if ( typeof val == "function" ) {
try {
2016-03-23 19:28:22 -04:00
val = command . enabled . call ( this , room , args ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
2016-10-12 16:42:43 -04:00
this . error ( 'command "' + cmd + '" enabled' , err ) ;
2015-02-10 01:34:23 -05:00
val = false ;
}
}
if ( ! val )
return false ;
}
this . log ( "Received Command: " + cmd , args , true ) ;
try {
2016-03-23 19:28:22 -04:00
output = command . call ( this , room , args ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
this . error ( 'command "' + cmd + '" runner: ' + err ) ;
output = "There was an error running the command." ;
}
if ( output )
this . room _message ( room , output ) ;
return true ;
}
FFZ . prototype . run _ffz _command = function ( text , room _id ) {
2015-01-20 01:53:18 -05:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
if ( ! text ) {
// Try to pop-up the menu.
var link = document . querySelector ( 'a.ffz-ui-toggle' ) ;
if ( link )
return link . click ( ) ;
text = "help" ;
}
var args = text . split ( " " ) ,
cmd = args . shift ( ) . toLowerCase ( ) ;
this . log ( "Received Command: " + cmd , args , true ) ;
2015-02-10 01:34:23 -05:00
var command = FFZ . ffz _commands [ cmd ] , output ;
2015-01-20 01:53:18 -05:00
if ( command ) {
try {
2016-03-23 19:28:22 -04:00
output = command . call ( this , room , args ) ;
2015-01-20 01:53:18 -05:00
} catch ( err ) {
this . log ( "Error Running Command - " + cmd + ": " + err , room ) ;
output = "There was an error running the command." ;
}
} else
output = 'There is no "' + cmd + '" command.' ;
if ( output )
this . room _message ( room , output ) ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . help = function ( room , args ) {
2015-01-20 01:53:18 -05:00
if ( args && args . length ) {
2015-02-10 01:34:23 -05:00
var command = FFZ . ffz _commands [ args [ 0 ] . toLowerCase ( ) ] ;
2015-01-20 01:53:18 -05:00
if ( ! command )
return 'There is no "' + args [ 0 ] + '" command.' ;
else if ( ! command . help )
return 'No help is available for the command "' + args [ 0 ] + '".' ;
else
return command . help ;
}
var cmds = [ ] ;
2015-02-10 01:34:23 -05:00
for ( var c in FFZ . ffz _commands )
FFZ . ffz _commands . hasOwnProperty ( c ) && cmds . push ( c ) ;
2015-01-20 01:53:18 -05:00
return "The available commands are: " + cmds . join ( ", " ) ;
}
2015-02-10 01:34:23 -05:00
FFZ . ffz _commands . help . help = "Usage: /ffz help [command]\nList available commands, or show help for a specific command." ;
2015-01-20 01:53:18 -05:00
// --------------------
// Room Management
// --------------------
2016-05-22 14:08:11 -04:00
FFZ . prototype . update _room _important = function ( id , controller ) {
var Chat = controller || utils . ember _lookup ( 'controller:chat' ) ,
2016-07-13 02:06:50 -04:00
current _room = Chat && Chat . get ( 'currentChannelRoom' ) ,
2016-05-22 14:08:11 -04:00
room = this . rooms [ id ] ;
if ( ! room )
return ;
2016-07-13 02:06:50 -04:00
room . important = ( room . room && current _room === room . room ) || ( current _room && current _room . ffz _host _target === id ) || ( room . room && room . room . get ( 'isGroupRoom' ) ) || ( this . settings . pinned _rooms . indexOf ( id ) !== - 1 ) ;
2016-05-22 14:08:11 -04:00
} ;
2016-10-18 14:06:20 -04:00
FFZ . prototype . _update _room _badge _css = function ( room _id ) {
var room = this . rooms [ room _id ] ,
badges = room && room . badges || { } ,
output = [ ] ;
2016-11-21 20:36:17 -05:00
// For rooms that don't have sub badges set.
2017-07-13 01:51:36 -04:00
/ * i f ( b a d g e s & & ! b a d g e s . s u b s c r i b e r ) {
2016-11-21 20:36:17 -05:00
var BadgeService = utils . ember _lookup ( 'service:badges' ) ,
global = BadgeService && BadgeService . badgeCollection && BadgeService . badgeCollection . global ;
badges . subscriber = global . subscriber ;
if ( badges . subscriber ) {
badges . subscriber . allow _invert = true ;
badges . subscriber . invert _invert = true ;
}
2017-07-13 01:51:36 -04:00
} * /
2016-11-21 20:36:17 -05:00
2016-10-18 14:06:20 -04:00
for ( var badge _id in badges ) {
var versions = badges [ badge _id ] && badges [ badge _id ] . versions || { } ;
for ( var version in versions )
output . push ( utils . room _badge _css ( room _id , badge _id , version , versions [ version ] ) ) ;
}
2017-07-13 01:51:36 -04:00
// Loyalty Badges Disabled
2016-11-21 20:36:17 -05:00
if ( badges . subscriber ) {
var versions = badges . subscriber . versions || { } ,
lowest = versions [ 0 ] || versions [ 1 ] ;
if ( lowest )
output . push ( utils . room _badge _css ( room _id , badge _id , Infinity , lowest ) ) ;
}
2016-10-18 14:06:20 -04:00
utils . update _css ( this . _badge _style , 'twitch-room-' + room _id , output . join ( '' ) ) ;
}
2016-10-20 16:41:04 -04:00
FFZ . prototype . add _room = function ( room _id , room ) {
if ( this . rooms [ room _id ] )
return this . log ( "Tried to add existing room: " + room _id ) ;
2015-01-20 01:53:18 -05:00
2016-10-20 16:41:04 -04:00
this . log ( "Adding Room: " + room _id ) ;
2015-01-20 01:53:18 -05:00
// Create a basic data table for this room.
2016-12-07 17:58:05 -05:00
var data = this . rooms [ room _id ] = {
id : room _id ,
room : room ,
users : { } ,
sets : [ ] ,
ext _sets : [ ] ,
css : null ,
needs _history : false } ;
2015-01-20 01:53:18 -05:00
2016-10-20 16:41:04 -04:00
if ( this . follow _sets && this . follow _sets [ room _id ] ) {
data . extra _sets = this . follow _sets [ room _id ] ;
delete this . follow _sets [ room _id ] ;
2015-07-04 17:06:36 -04:00
for ( var i = 0 ; i < data . extra _sets . length ; i ++ ) {
var sid = data . extra _sets [ i ] ,
set = this . emote _sets && this . emote _sets [ sid ] ;
if ( set ) {
2016-10-20 16:41:04 -04:00
if ( set . users . indexOf ( room _id ) === - 1 )
set . users . push ( room _id ) ;
2016-12-19 21:33:45 -05:00
room . ffzRetokenizeUser ( ) ;
2015-07-04 17:06:36 -04:00
continue ;
}
this . load _set ( sid , function ( success , data ) {
2016-12-19 21:33:45 -05:00
if ( success ) {
if ( data . users . indexOf ( room _id ) === - 1 )
data . users . push ( room _id ) ;
room . ffzRetokenizeUser ( ) ;
}
2015-07-04 17:06:36 -04:00
} ) ;
}
}
2016-10-16 15:05:11 -04:00
// Store the badges for this room now if we have them.
var bs = utils . ember _lookup ( 'service:badges' ) ;
2016-10-20 16:41:04 -04:00
if ( bs && bs . badgeCollection && bs . badgeCollection . channel && bs . badgeCollection . channel . broadcasterName === room _id ) {
2016-10-16 15:05:11 -04:00
data . badges = bs . badgeCollection . channel ;
2016-10-20 16:41:04 -04:00
this . _update _room _badge _css ( room _id ) ;
2016-10-18 14:06:20 -04:00
}
2016-10-16 15:05:11 -04:00
2016-10-05 01:31:10 -04:00
// Look up if the room has moderation logs.
var f = this ;
2016-10-20 16:41:04 -04:00
this . ws _send ( "has_logs" , room _id , function ( success , response ) {
2016-10-05 01:31:10 -04:00
if ( ! success )
return ;
data . has _logs = response . has _logs ;
data . log _source = response . log _source ;
2017-01-30 16:36:33 -05:00
if ( f . settings . mod _card _history === 1 )
data . user _history = undefined ;
2016-10-05 01:31:10 -04:00
} , true ) ;
2016-05-22 14:08:11 -04:00
// Is the room important?
2016-10-20 16:41:04 -04:00
this . update _room _important ( room _id ) ;
2016-05-22 14:08:11 -04:00
2016-10-20 16:41:04 -04:00
if ( data . important )
2016-05-22 14:08:11 -04:00
// Let the server know where we are.
2016-10-20 16:41:04 -04:00
this . ws _sub ( "room." + room _id ) ;
2015-01-20 01:53:18 -05:00
2016-05-22 14:08:11 -04:00
// Do we want history?
2016-09-30 13:09:03 -04:00
/ * i f ( ! t h i s . h a s _ b t t v & & t h i s . s e t t i n g s . c h a t _ h i s t o r y & & r o o m & & ( r o o m . g e t ( ' m e s s a g e s . l e n g t h ' ) | | 0 ) < 1 0 ) {
2016-05-22 14:08:11 -04:00
if ( ! this . ws _send ( "chat_history" , [ id , 25 ] , this . _load _history . bind ( this , id ) ) )
data . needs _history = true ;
2016-09-30 13:09:03 -04:00
} * /
2015-06-10 18:46:04 -04:00
2016-05-22 14:08:11 -04:00
2015-07-04 17:06:36 -04:00
// Why don't we set the scrollback length, too?
2016-12-10 22:24:31 -05:00
room . set ( 'messageBufferSize' , this . settings . scrollback _length + ( ( this . _roomv && ! this . _roomv . get ( 'stuckToBottom' ) && this . _roomv . get ( 'room.id' ) === room _id ) ? 150 : 0 ) ) ;
2015-07-04 17:06:36 -04:00
2015-10-24 22:44:00 -04:00
// Load the room's data from the API.
2016-10-20 16:41:04 -04:00
this . load _room ( room _id ) ;
2015-10-24 22:44:00 -04:00
// Announce this room to any extension callback functions.
2016-10-20 16:41:04 -04:00
this . api _trigger ( 'room-add' , room _id ) ;
2015-01-20 01:53:18 -05:00
}
2016-10-20 16:41:04 -04:00
FFZ . prototype . remove _room = function ( room _id ) {
var room = this . rooms [ room _id ] ;
2015-01-20 01:53:18 -05:00
if ( ! room )
return ;
2016-10-20 16:41:04 -04:00
this . log ( "Removing Room: " + room _id ) ;
2015-01-20 01:53:18 -05:00
// Remove the CSS
if ( room . css || room . moderator _badge )
2016-10-20 16:41:04 -04:00
utils . update _css ( this . _room _style , room _id , null ) ;
2015-01-20 01:53:18 -05:00
// Let the server know we're gone and delete our data for this room.
2016-10-20 16:41:04 -04:00
this . ws _unsub ( "room." + room _id ) ;
delete this . rooms [ room _id ] ;
2015-01-20 01:53:18 -05:00
// Clean up sets we aren't using any longer.
2015-06-05 03:59:28 -04:00
var set = this . emote _sets [ room . set ] ;
if ( set ) {
2016-10-20 16:41:04 -04:00
set . users . removeObject ( room _id ) ;
2015-06-05 03:59:28 -04:00
if ( ! this . global _sets . contains ( room . set ) && ! set . users . length )
this . unload _set ( room . set ) ;
2015-01-20 01:53:18 -05:00
}
2016-10-20 02:34:55 -04:00
2016-10-20 16:41:04 -04:00
this . api _trigger ( 'room-remove' , room _id ) ;
2015-01-20 01:53:18 -05:00
}
// --------------------
// Receiving Set Info
// --------------------
2015-06-05 03:59:28 -04:00
FFZ . prototype . load _room = function ( room _id , callback , tries ) {
var f = this ;
2015-12-12 13:28:35 -05:00
jQuery . getJSON ( constants . API _SERVER + "v1/room/" + room _id )
2015-06-05 03:59:28 -04:00
. done ( function ( data ) {
if ( data . sets ) {
for ( var key in data . sets )
data . sets . hasOwnProperty ( key ) && f . _load _set _json ( key , undefined , data . sets [ key ] ) ;
}
f . _load _room _json ( room _id , callback , data ) ;
} ) . fail ( function ( data ) {
if ( data . status == 404 )
return typeof callback == "function" && callback ( false ) ;
tries = ( tries || 0 ) + 1 ;
if ( tries < 10 )
return f . load _room ( room _id , callback , tries ) ;
return typeof callback == "function" && callback ( false ) ;
} ) ;
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . _load _room _json = function ( room _id , callback , data ) {
2015-06-05 03:59:28 -04:00
if ( ! data || ! data . room )
return typeof callback == "function" && callback ( false ) ;
data = data . room ;
2016-10-05 01:31:10 -04:00
// Apply the data we've received to the room data model.
var model = this . rooms [ room _id ] = this . rooms [ room _id ] || { } ;
for ( var key in data )
2017-06-11 13:30:37 -04:00
if ( key !== 'user_badges' && key !== 'users' && key !== 'room' && data . hasOwnProperty ( key ) )
2016-10-05 01:31:10 -04:00
model [ key ] = data [ key ] ;
2016-12-07 17:58:05 -05:00
// Merge the user data.
for ( var user _id in data . users ) {
var original = model . users [ user _id ] || { } ,
new _data = data . users [ user _id ] || { } ;
model . users [ user _id ] = {
sets : _ . uniq ( ( original . sets || [ ] ) . concat ( new _data . sets || [ ] ) ) ,
badges : _ . extend ( original . badges || { } , new _data . badges || { } )
}
}
2017-06-11 13:30:37 -04:00
// Merge badge data
for ( var badge _id in data . user _badges ) {
var badge = this . badges [ badge _id ] ;
if ( ! badge )
continue ;
for ( var l = data . user _badges [ badge _id ] , i = 0 , j = l . length ; i < j ; i ++ ) {
var user _id = l [ i ] ,
user = model . users [ user _id ] = model . users [ user _id ] || { } ,
badges = user . badges = user . badges || { } ;
badges [ badge . slot ] = { id : badge _id } ;
}
}
2015-01-20 01:53:18 -05:00
// Preserve the pointer to the Room instance.
2016-10-05 01:31:10 -04:00
/ * i f ( t h i s . r o o m s [ r o o m _ i d ] )
2015-01-20 01:53:18 -05:00
data . room = this . rooms [ room _id ] . room ;
2015-07-04 17:06:36 -04:00
// Preserve everything else.
for ( var key in this . rooms [ room _id ] ) {
if ( key !== 'room' && this . rooms [ room _id ] . hasOwnProperty ( key ) && ! data . hasOwnProperty ( key ) )
data [ key ] = this . rooms [ room _id ] [ key ] ;
}
2015-06-10 18:46:04 -04:00
data . needs _history = this . rooms [ room _id ] && this . rooms [ room _id ] . needs _history || false ;
2016-10-05 01:31:10 -04:00
this . rooms [ room _id ] = data ; * /
2015-01-20 01:53:18 -05:00
2016-12-07 17:58:05 -05:00
if ( model . css || model . mod _urls )
2017-08-16 13:24:55 -04:00
utils . update _css ( this . _room _style , room _id , ( this . settings . custom _badge _images ? utils . moderator _css ( model ) : '' ) + ( model . css || "" ) ) ;
2015-01-20 01:53:18 -05:00
2016-10-05 01:31:10 -04:00
if ( ! this . emote _sets . hasOwnProperty ( model . set ) )
this . load _set ( model . set , function ( success , set ) {
2015-07-04 17:06:36 -04:00
if ( set . users . indexOf ( room _id ) === - 1 )
set . users . push ( room _id ) ;
} ) ;
2016-10-05 01:31:10 -04:00
else if ( this . emote _sets [ model . set ] . users . indexOf ( room _id ) === - 1 )
this . emote _sets [ model . set ] . users . push ( room _id ) ;
2015-01-20 01:53:18 -05:00
this . update _ui _link ( ) ;
2016-10-05 01:31:10 -04:00
if ( model . set )
this . rerender _feed _cards ( model . set ) ;
2016-04-11 18:57:25 -04:00
2015-01-20 01:53:18 -05:00
if ( callback )
2016-10-05 01:31:10 -04:00
callback ( true , model ) ;
2015-01-20 01:53:18 -05:00
}
// --------------------
// Ember Modifications
// --------------------
FFZ . prototype . _modify _room = function ( room ) {
var f = this ;
room . reopen ( {
2015-07-04 17:06:36 -04:00
slowWaiting : false ,
2015-07-29 01:03:10 -04:00
slow : 0 ,
2015-07-04 17:06:36 -04:00
2016-05-12 00:11:50 -04:00
ffz _banned : false ,
2015-07-06 00:09:21 -04:00
mru _list : [ ] ,
2017-06-11 13:30:37 -04:00
ffz _recent _highlights : [ ] ,
ffz _recent _highlights _unread : 0 ,
2015-07-06 00:09:21 -04:00
2016-10-16 15:05:11 -04:00
ffzUpdateBadges : function ( ) {
2016-10-20 02:34:55 -04:00
if ( this . get ( 'isGroupRoom' ) )
return ;
2016-10-16 15:05:11 -04:00
var room _name = this . get ( 'id' ) ,
room _id = this . get ( 'roomProperties._id' ) ,
ffz _room = f . rooms && f . rooms [ room _name ] ;
if ( ! ffz _room || ! room _id || ffz _room . badges )
return ;
fetch ( "https://badges.twitch.tv/v1/badges/channels/" + room _id + "/display?language=" + ( Twitch . receivedLanguage || "en" ) , {
headers : {
'Client-ID' : constants . CLIENT _ID
}
} ) . then ( utils . json ) . then ( function ( data ) {
ffz _room . badges = data && data . badge _sets ;
2016-10-18 14:06:20 -04:00
f . _update _room _badge _css ( room _name ) ;
2016-10-16 15:05:11 -04:00
} ) ;
} . observes ( 'roomProperties._id' ) ,
2016-05-13 23:56:59 -04:00
updateWait : function ( value , was _banned , update ) {
2015-07-04 17:06:36 -04:00
var wait = this . get ( 'slowWait' ) || 0 ;
this . set ( 'slowWait' , value ) ;
if ( wait < 1 && value > 0 ) {
2015-07-13 21:52:44 -04:00
if ( this . _ffz _wait _timer )
clearTimeout ( this . _ffz _wait _timer ) ;
this . _ffz _wait _timer = setTimeout ( this . ffzUpdateWait . bind ( this ) , 1000 ) ;
2016-10-25 14:44:22 -04:00
! update && f . _chati && Ember . propertyDidChange ( f . _chati , 'submitButtonText' ) ;
2016-05-13 23:56:59 -04:00
! update && f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
2015-07-04 17:06:36 -04:00
} else if ( ( wait > 0 && value < 1 ) || was _banned ) {
this . set ( 'ffz_banned' , false ) ;
2016-10-25 14:44:22 -04:00
! update && f . _chati && Ember . propertyDidChange ( f . _chati , 'submitButtonText' ) ;
2016-05-13 23:56:59 -04:00
! update && f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
2015-07-04 17:06:36 -04:00
}
} ,
ffzUpdateWait : function ( ) {
2015-07-13 21:52:44 -04:00
this . _ffz _wait _timer = undefined ;
2015-07-04 17:06:36 -04:00
var wait = this . get ( 'slowWait' ) || 0 ;
if ( wait < 1 )
return ;
this . set ( 'slowWait' , -- wait ) ;
2016-10-25 14:44:22 -04:00
f . _chati && Ember . propertyDidChange ( f . _chati , 'submitButtonText' ) ;
2015-07-04 17:06:36 -04:00
if ( wait > 0 )
2015-07-13 21:52:44 -04:00
this . _ffz _wait _timer = setTimeout ( this . ffzUpdateWait . bind ( this ) , 1000 ) ;
2015-07-04 17:06:36 -04:00
else {
this . set ( 'ffz_banned' , false ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
} ,
2015-12-12 13:28:35 -05:00
ffzScheduleDestroy : function ( ) {
if ( this . _ffz _destroy _timer )
return ;
var t = this ;
this . _ffz _destroy _timer = setTimeout ( function ( ) {
t . _ffz _destroy _timer = null ;
t . ffzCheckDestroy ( ) ;
} , 5000 ) ;
} ,
ffzCheckDestroy : function ( ) {
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ,
2017-01-27 23:28:30 -05:00
current _vod = f . _vodc && f . _vodc . get ( 'channel.name' ) ,
2015-12-12 13:28:35 -05:00
user = f . get _user ( ) ,
room _id = this . get ( 'id' ) ;
2016-08-09 20:45:28 -04:00
// Don't destroy the room if it's still relevant.
2017-01-27 23:28:30 -05:00
if ( ( current _vod === room _id ) || ( Chat && Chat . get ( 'currentChannelRoom' ) === this ) || ( user && user . login === room _id ) || ( f . _chatv && f . _chatv . _ffz _host === room _id ) || ( f . settings . pinned _rooms && f . settings . pinned _rooms . indexOf ( room _id ) !== - 1 ) )
2016-08-09 20:45:28 -04:00
return ;
2015-12-12 13:28:35 -05:00
this . destroy ( ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzUpdateStatus : function ( ) {
if ( f . _roomv )
f . _roomv . ffzUpdateStatus ( ) ;
2016-12-19 21:33:45 -05:00
} . observes ( 'r9k' , 'subsOnly' , 'emoteOnly' , 'slow' , 'ffz_banned' , 'followersOnly' ) ,
2015-07-29 01:03:10 -04:00
2016-05-06 02:23:12 -04:00
2015-07-29 01:03:10 -04:00
// User Level
ffzUserLevel : function ( ) {
if ( this . get ( 'isStaff' ) )
return 5 ;
else if ( this . get ( 'isAdmin' ) )
return 4 ;
else if ( this . get ( 'isBroadcaster' ) )
return 3 ;
else if ( this . get ( 'isGlobalModerator' ) )
return 2 ;
else if ( this . get ( 'isModerator' ) )
return 1 ;
return 0 ;
} . property ( 'id' , 'chatLabels.[]' ) ,
2015-07-04 17:06:36 -04:00
2015-01-20 20:25:26 -05:00
// Track which rooms the user is currently in.
2015-01-20 01:53:18 -05:00
init : function ( ) {
this . _super ( ) ;
2015-08-09 23:32:29 -07:00
2015-02-10 01:34:23 -05:00
try {
f . add _room ( this . id , this ) ;
2017-06-11 13:30:37 -04:00
this . set ( "ffz_chatters" , [ ] ) ;
2016-07-13 02:06:50 -04:00
this . set ( "ffz_ids" , this . get ( 'ffz_ids' ) || { } ) ;
2016-09-09 17:34:20 -04:00
this . set ( "ffz_last_notices" , this . get ( 'ffz_last_notices' ) || { } ) ;
2015-02-10 01:34:23 -05:00
} catch ( err ) {
f . error ( "add_room: " + err ) ;
}
2015-01-20 01:53:18 -05:00
} ,
willDestroy : function ( ) {
2016-09-09 17:34:20 -04:00
this . get ( "pubsub" ) . set ( "ffz_teardown_target" , this . get ( 'roomProperties._id' ) ) ;
2015-01-20 01:53:18 -05:00
this . _super ( ) ;
2016-09-09 17:34:20 -04:00
this . get ( "pubsub" ) . set ( "ffz_teardown_target" , null ) ;
2015-08-09 23:32:29 -07:00
2015-02-10 01:34:23 -05:00
try {
f . remove _room ( this . id ) ;
} catch ( err ) {
f . error ( "remove_room: " + err ) ;
}
2015-01-20 01:53:18 -05:00
} ,
2016-10-27 14:32:35 -04:00
friendsInSameRoom : function ( source ) {
if ( f . settings . sidebar _disable _friends || f . settings . disable _friend _notices )
return [ ] ;
var room _name = this . get ( 'roomProperties.id' ) ;
return _ . filter ( this . _super ( source ) , function ( x ) {
return x && x [ 'id' ] !== room _name ;
} ) ;
} ,
addFriendsWatchingMessage : function ( msg ) {
2017-05-09 16:49:24 -04:00
if ( f . settings . friend _notifications && ! document . hasFocus ( ) ) {
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
if ( Chat && Chat . get ( 'currentChannelRoom' ) === this )
f . show _notification (
msg . replace ( / *VoHiYo$/g , '' ) ,
( this . get ( 'channel.display_name' ) || this . get ( 'id' ) ) + " - Twitch" ,
"ffz_watching_notice" ,
( this . settings . notification _timeout * 1000 ) ,
function ( ) {
window . focus ( ) ;
} ,
null ,
'https://static-cdn.jtvnw.net/emoticons/v1/81274/3.0' ) ;
}
2017-04-21 20:02:27 -04:00
2016-10-27 14:32:35 -04:00
this . addMessage ( {
2017-04-10 15:03:05 -04:00
style : 'admin' + ( f . has _bttv _6 ? '' : ' friend-watching' ) ,
2016-10-27 14:32:35 -04:00
message : msg ,
tags : {
emotes : tmimotes && tmimotes ( this . get ( 'userEmotes' ) . tryParseEmotes ( msg ) )
}
} ) ;
} ,
2016-09-09 17:34:20 -04:00
addChannelModerationMessage : function ( event ) {
// Throw out messages that are for other rooms.
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" ) )
return ;
2016-09-30 13:09:03 -04:00
if ( ! f . settings . get _twitch ( 'showModerationActions' ) )
return ;
2016-09-09 17:34:20 -04:00
var target _notice = NOTICE _MAPPING [ event . moderation _action ] ;
if ( target _notice ) {
2017-02-26 19:11:28 -05:00
var last _notice ;
if ( ! this . ffz _last _notices )
last _notice = null ;
else if ( Array . isArray ( target _notice ) )
for ( var i = 0 ; i < target _notice . length ; i ++ ) {
var tn = this . ffz _last _notices [ target _notice [ i ] ] ;
if ( tn ) {
last _notice = tn ;
break ;
}
}
else
last _notice = this . ffz _last _notices [ target _notice ] ;
2016-09-09 17:34:20 -04:00
if ( last _notice && ! last _notice . has _owner ) {
last _notice . message += ' (By: ' + event . created _by + ')' ;
last _notice . has _owner = true ;
last _notice . cachedTokens = undefined ;
if ( last _notice . _line )
2016-09-30 13:09:03 -04:00
Ember . propertyDidChange ( last _notice . _line , 'ffzTokenizedMessage' ) ;
2016-09-09 17:34:20 -04:00
} else {
var waiting = this . ffz _waiting _notices = this . ffz _waiting _notices || { } ;
waiting [ target _notice ] = event . created _by ;
}
2016-09-30 13:09:03 -04:00
} else
2016-09-09 17:34:20 -04:00
this . _super ( event ) ;
} ,
addLoginModerationMessage : function ( event ) {
2016-10-14 20:43:34 -04:00
// Throw out messages that are for other rooms or that don't have topics.
2016-09-09 17:34:20 -04:00
var room _id = '.' + this . get ( "roomProperties._id" ) ;
2016-10-14 20:43:34 -04:00
if ( ! event . topic || event . topic . substr ( - room _id . length ) !== room _id || event . created _by === this . get ( "session.userData.login" ) )
2016-09-09 17:34:20 -04:00
return ;
2017-03-27 18:03:10 -04:00
//f.log("Login Moderation for " + this.get('id') + ' [' + room_id + ']', event);
2016-09-30 13:09:03 -04:00
2016-09-09 17:34:20 -04:00
// In case we get unexpected input, do the other thing.
2017-04-10 15:03:05 -04:00
if ( f . has _bttv _6 || [ "ban" , "unban" , "timeout" , "untimeout" ] . indexOf ( event . moderation _action ) === - 1 )
2016-09-09 17:34:20 -04:00
return this . _super ( event ) ;
2016-10-14 20:43:34 -04:00
var msg _id ,
reason = event . args [ 2 ] ,
2016-10-27 12:49:31 -04:00
duration = event . moderation _action === 'unban' ? - Infinity :
event . moderation _action === 'untimeout' ? - 1 :
event . args [ 1 ] ;
2016-10-14 20:43:34 -04:00
if ( typeof duration === "string" )
duration = parseInt ( duration ) ;
2016-09-09 17:34:20 -04:00
2016-10-14 20:43:34 -04:00
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 ) ;
2016-09-09 17:34:20 -04:00
} ,
2016-10-14 20:43:34 -04:00
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 ) ;
2017-02-03 02:01:18 -05:00
this . addUserHistory ( message , true ) ;
2016-10-14 20:43:34 -04:00
}
} ,
2017-02-03 02:01:18 -05:00
addUserHistory : function ( message , dont _copy ) {
2016-10-14 20:43:34 -04:00
var room _id = this . get ( 'id' ) ,
2017-01-30 16:36:33 -05:00
is _group = this . get ( 'isGroupRoom' ) ,
setting = f . settings . mod _card _history ,
2016-10-14 20:43:34 -04:00
ffz _room = f . rooms [ room _id ] ;
2017-01-30 16:36:33 -05:00
if ( is _group || ! ffz _room || setting === 0 || ( setting === 1 && ffz _room . has _logs ) )
2016-10-14 20:43:34 -04:00
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 || { } ,
2017-01-30 16:36:33 -05:00
user _history = chat _history [ username ] = chat _history [ username ] || [ ] ,
// Don't store any computed values that would take a lot of memory.
old _tags = message . tags || { } ,
cache _object = {
date : message . date ,
from : message . from ,
room : message . room ,
message : message . message ,
style : message . style ,
tags : {
emotes : old _tags . emotes ,
bits : old _tags . bits ,
'display-name' : old _tags [ 'display-name' ] ,
id : old _tags . id
}
} ;
2016-10-14 20:43:34 -04:00
2017-02-03 02:01:18 -05:00
if ( dont _copy )
cache _object = message ;
2016-10-14 20:43:34 -04:00
if ( historical ) {
if ( user _history . length >= 20 )
return ;
2017-01-30 16:36:33 -05:00
user _history . unshift ( cache _object ) ;
2016-10-14 20:43:34 -04:00
} else {
2017-01-30 16:36:33 -05:00
user _history . push ( cache _object ) ;
2016-10-14 20:43:34 -04:00
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 ) ,
2017-01-30 16:36:33 -05:00
line = f . _build _mod _card _history ( cache _object , f . _mod _card ) ;
2016-10-14 20:43:34 -04:00
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 ) ;
}
}
} ,
2016-05-13 23:56:59 -04:00
2016-10-14 20:43:34 -04:00
clearMessages : function ( user , tags , disable _log ) {
var t = this ;
2015-06-10 18:46:04 -04:00
if ( user ) {
2016-05-20 17:30:34 -04:00
var duration = Infinity ,
2016-05-13 23:56:59 -04:00
reason = undefined ,
2016-09-09 17:34:20 -04:00
moderator = undefined ,
2016-11-19 01:59:03 -05:00
msg _id = tags && tags [ 'target-msg-id' ] ,
2016-05-13 23:56:59 -04:00
current _user = f . get _user ( ) ,
is _me = current _user && current _user . login === user ;
// Read the ban duration and reason from the message tags.
2016-09-09 17:34:20 -04:00
if ( tags && tags [ 'ban-duration' ] ) {
duration = tags [ 'ban-duration' ] ;
if ( typeof duration === 'string' )
duration = parseInt ( duration ) ;
2016-05-20 17:30:34 -04:00
2016-09-09 17:34:20 -04:00
if ( isNaN ( duration ) )
duration = Infinity ;
}
2016-05-13 23:56:59 -04:00
if ( tags && tags [ 'ban-reason' ] && ( is _me || t . get ( 'isModeratorOrHigher' ) ) )
reason = tags [ 'ban-reason' ] ;
2016-09-09 17:34:20 -04:00
if ( tags && tags [ 'ban-moderator' ] && ( is _me || t . get ( 'isModeratorOrHigher' ) ) )
moderator = tags [ 'ban-moderator' ] ;
2016-07-13 02:06:50 -04:00
// Is there a UUID on the end of the ban reason?
2016-11-19 01:59:03 -05:00
if ( ! msg _id && reason ) {
2016-07-13 02:06:50 -04:00
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 = undefined ;
}
}
2016-05-13 23:56:59 -04:00
// If we were banned, set the state and update the UI.
if ( is _me ) {
t . set ( 'ffz_banned' , true ) ;
2016-10-14 20:43:34 -04:00
if ( duration )
if ( isFinite ( duration ) )
t . updateWait ( duration ) ;
else {
t . set ( 'slowWait' , 0 ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
2016-05-13 23:56:59 -04:00
}
// Mark the user as recently banned.
if ( ! t . ffzRecentlyBanned )
t . ffzRecentlyBanned = [ ] ;
t . ffzRecentlyBanned . push ( user ) ;
while ( t . ffzRecentlyBanned . length > 100 )
t . ffzRecentlyBanned . shift ( ) ;
2016-07-13 02:06:50 -04:00
// Are we deleting a specific message?
if ( msg _id && this . ffz _ids ) {
var msg = this . ffz _ids [ msg _id ] ;
if ( msg && msg . from === user ) {
msg . ffz _deleted = true ;
if ( ! f . settings . prevent _clear )
msg . deleted = true ;
if ( f . settings . remove _deleted )
if ( msg . pending )
msg . removed = true ;
else {
var msgs = t . get ( 'messages' ) ,
total = msgs . get ( 'length' ) ,
i = total ;
while ( i -- ) {
var msg = msgs . get ( i ) ;
if ( msg . tags && msg . tags . id === msg _id ) {
msgs . removeAt ( i ) ;
delete this . ffz _ids [ msg _id ] ;
2016-09-09 17:34:20 -04:00
var notice _type = msg . tags && msg . tags [ 'msg-id' ] ;
if ( notice _type && this . ffz _last _notices && this . ffz _last _notices [ notice _type ] === msg )
delete this . ffz _last _notices [ notice _type ] ;
2016-07-13 02:06:50 -04:00
break ;
}
}
}
2015-08-14 13:21:57 -04:00
2016-07-13 02:06:50 -04:00
if ( msg . _line ) {
Ember . propertyDidChange ( msg . _line , 'msgObject.ffz_deleted' ) ;
Ember . propertyDidChange ( msg . _line , 'msgObject.deleted' ) ;
2015-07-29 01:03:10 -04:00
}
2015-08-14 13:21:57 -04:00
2016-07-13 02:06:50 -04:00
} else if ( msg . from !== user )
f . log ( "Banned Message ID #" + msg _id + " not owned by: " + user ) ;
else
f . log ( "Banned Message ID #" + msg _id + " not found in chat." ) ;
2015-07-04 17:06:36 -04:00
2016-07-13 02:06:50 -04:00
} else {
// Delete all messages from this user / chat.
// Delete Visible Messages
var msgs = t . get ( 'messages' ) ,
total = msgs . get ( 'length' ) ,
i = total ,
removed = 0 ;
2016-05-13 23:56:59 -04:00
2015-08-09 23:32:29 -07:00
while ( i -- ) {
var msg = msgs . get ( i ) ;
2016-07-13 02:06:50 -04:00
if ( msg . from === user ) {
if ( f . settings . remove _deleted ) {
// Remove this message from the ID tracker.
2016-09-09 17:34:20 -04:00
var 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 ] ;
2016-07-13 02:06:50 -04:00
msgs . removeAt ( i ) ;
removed ++ ;
continue ;
}
t . set ( 'messages.' + i + '.ffz_deleted' , true ) ;
if ( ! f . settings . prevent _clear )
t . set ( 'messages.' + i + '.deleted' , true ) ;
}
}
// Delete Panding Messages
if ( t . ffzPending ) {
msgs = t . ffzPending ;
i = msgs . length ;
while ( i -- ) {
var msg = msgs . get ( i ) ;
if ( msg . from !== user ) continue ;
msg . ffz _deleted = true ;
msg . deleted = ! f . settings . prevent _clear ;
msg . removed = f . settings . remove _deleted ;
}
2015-08-09 23:32:29 -07:00
}
}
2016-10-14 20:43:34 -04:00
if ( ! disable _log )
this . addBanNotice ( user , duration , reason , null , msg _id ) ;
2016-05-20 17:30:34 -04:00
2015-06-10 18:46:04 -04:00
} else {
if ( f . settings . prevent _clear )
2016-10-14 20:43:34 -04:00
t . addMessage ( {
style : 'admin' ,
message : "A moderator's attempt to clear chat was ignored." ,
tags : {
'msg-id' : 'clear_chat'
}
} ) ;
2015-06-10 18:46:04 -04:00
else {
var msgs = t . get ( "messages" ) ;
t . set ( "messages" , [ ] ) ;
t . addMessage ( {
style : 'admin' ,
2016-10-14 20:43:34 -04:00
message : i18n ( "Chat was cleared by a moderator" ) ,
tags : {
'msg-id' : 'clear_chat'
}
2015-06-10 18:46:04 -04:00
} ) ;
}
}
} ,
2015-08-09 23:32:29 -07:00
// Artificial chat delay
2016-08-09 20:45:28 -04:00
ffz _chat _delay : function ( ) {
var val = f . settings . chat _delay ;
if ( val !== - 1 )
return val ;
val = this . get ( 'roomProperties.chat_delay_duration' ) ;
return ( Number . isNaN ( val ) || ! Number . isFinite ( val ) || this . get ( 'isModeratorOrHigher' ) ) ? 0 : val ;
} . property ( 'roomProperties.chat_delay_duration' , 'isModeratorOrHigher' ) ,
ffz _update _display : function ( ) {
if ( f . _roomv )
f . _roomv . ffzUpdateStatus ( ) ;
} . observes ( 'roomProperties.chat_delay_duration' ) ,
2015-06-10 18:46:04 -04:00
pushMessage : function ( msg ) {
2016-08-09 20:45:28 -04:00
if ( f . settings . chat _batching !== 0 || this . get ( 'ffz_chat_delay' ) !== 0 || ( this . ffzPending && this . ffzPending . length ) ) {
2015-08-14 13:21:57 -04:00
if ( ! this . ffzPending )
2015-08-09 23:32:29 -07:00
this . ffzPending = [ ] ;
2015-11-19 02:45:56 -05:00
var now = msg . time = Date . now ( ) ;
2016-07-13 02:06:50 -04:00
msg . pending = true ;
2015-08-09 23:32:29 -07:00
this . ffzPending . push ( msg ) ;
2015-08-14 13:21:57 -04:00
this . ffzSchedulePendingFlush ( now ) ;
2015-08-09 23:32:29 -07:00
} else {
2016-09-30 13:09:03 -04:00
this . ffzPushMessages ( [ msg ] ) ;
}
} ,
ffzPushMessages : function ( messages ) {
var new _messages = [ ] ,
2017-06-11 13:30:37 -04:00
new _unread = 0 ,
highlight _messages = [ ] ,
highlight _unread = 0 ;
2016-09-30 13:09:03 -04:00
for ( var i = 0 ; i < messages . length ; i ++ ) {
var msg = messages [ i ] ;
if ( this . shouldShowMessage ( msg ) && this . ffzShouldShowMessage ( msg ) ) {
new _messages . push ( msg ) ;
if ( ! ( msg . tags && msg . tags . historical ) && msg . style !== "admin" && msg . style !== "whisper" ) {
2017-06-11 13:30:37 -04:00
if ( msg . ffz _has _mention ) {
2016-09-30 13:09:03 -04:00
this . ffz _last _mention = Date . now ( ) ;
2017-06-11 13:30:37 -04:00
highlight _messages . push ( msg ) ;
highlight _unread ++ ;
}
2016-09-30 13:09:03 -04:00
new _unread ++ ;
}
}
}
if ( ! new _messages . length )
return ;
var room _messages = this . get ( "messages" ) ,
2016-10-11 21:41:35 -04:00
raw _remove = room _messages . length + new _messages . length > this . messageBufferSize ?
Math . max ( 0 , room _messages . length - this . messageBufferSize ) + new _messages . length : 0 ,
to _remove = raw _remove - raw _remove % 2 ,
2016-10-14 20:43:34 -04:00
removed = room _messages . slice ( 0 , to _remove ) ,
2016-10-11 21:41:35 -04:00
trimmed = room _messages . slice ( to _remove , room _messages . length ) ;
2016-09-30 13:09:03 -04:00
2016-10-14 20:43:34 -04:00
// 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 ] ;
}
2016-09-30 13:09:03 -04:00
var earliest _message ;
for ( var i = 0 ; i < trimmed . length ; i ++ )
if ( trimmed [ i ] . date ) {
earliest _message = trimmed [ i ] . date ;
break ;
}
for ( var i = 0 ; i < new _messages . length ; i ++ ) {
var msg = new _messages [ i ] ;
if ( msg . tags && msg . tags . historical ) {
// Add a warning about really old messages.
if ( earliest _message ) {
var age = earliest _message - msg . date ;
if ( age > 300000 )
trimmed . unshift ( {
color : '#755000' ,
date : msg . date ,
from : 'frankerfacez_admin' ,
style : 'admin' ,
message : '(Last message is ' + utils . human _time ( age / 1000 ) + ' old.)' ,
room : msg . room ,
from _server : true
} ) ;
}
trimmed . unshift ( msg ) ;
earliest _message = null ;
} else
trimmed . push ( msg ) ;
}
this . set ( "messages" , trimmed ) ;
if ( new _unread ) {
this . incrementProperty ( "unreadCount" , new _unread ) ;
this . ffz _last _activity = Date . now ( ) ;
2015-08-09 23:32:29 -07:00
}
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
if ( highlight _unread ) {
// API Stuff
f . api _trigger ( 'room-recent-highlights' , highlight _messages ) ;
if ( f . settings . recent _highlights && highlight _messages . length ) {
var old _highlights = this . get ( 'ffz_recent_highlights' ) || [ ] ,
raw _remove = old _highlights . length + highlight _messages . length > f . settings . recent _highlight _count ?
Math . max ( 0 , old _highlights . length - f . settings . recent _highlight _count ) + highlight _messages . length : 0 ,
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
to _remove = raw _remove % 2 ,
trimmed = old _highlights . slice ( to _remove , old _highlights . length ) . concat ( highlight _messages ) ,
el = f . _roomv && f . _roomv . get ( 'ffz_recent_el' ) ;
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
this . set ( 'ffz_recent_highlights' , trimmed ) ;
this . incrementProperty ( 'ffz_recent_highlights_unread' , highlight _messages . length ) ;
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
if ( el && f . _roomv . get ( 'room' ) === this ) {
var was _at _bottom = el . scrollTop >= ( el . scrollHeight - el . clientHeight ) ;
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
while ( el . childElementCount && to _remove -- )
el . removeChild ( el . firstElementChild ) ;
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
for ( var i = 0 ; i < highlight _messages . length ; i ++ )
el . appendChild ( f . _build _mod _card _history ( highlight _messages [ i ] , null , true ) ) ;
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
if ( was _at _bottom )
el . scrollTop = el . scrollHeight ;
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
if ( el . dataset . docked ) {
el = f . _roomv . get ( 'ffz_recent_count_el' ) ;
if ( el )
el . textContent = utils . format _unread ( this . get ( 'ffz_recent_highlights_unread' ) ) ;
}
2017-06-11 13:30:37 -04:00
2017-08-16 13:24:55 -04:00
}
2017-06-11 13:30:37 -04:00
}
}
2015-08-09 23:32:29 -07:00
} ,
2015-08-14 13:21:57 -04:00
ffzSchedulePendingFlush : function ( now ) {
// Instead of just blindly looping every x seconds, we want to calculate the time until
// the next message should be displayed, and then set the timeout for that. We'll
// end up looping a bit more frequently, but it'll make chat feel more responsive.
2015-11-01 17:28:19 -05:00
// If we have a pending flush, don't reschedule. It wouldn't change.
2015-08-14 13:21:57 -04:00
if ( this . _ffz _pending _flush )
2015-11-01 17:28:19 -05:00
return ;
2015-08-14 13:21:57 -04:00
if ( this . ffzPending && this . ffzPending . length ) {
2015-11-01 17:28:19 -05:00
// We need either the amount of chat delay past the first message, if chat_delay is on, or the
// amount of time from the last batch.
2015-11-19 02:45:56 -05:00
now = now || Date . now ( ) ;
2016-06-02 20:04:40 -04:00
var t = this ,
2016-08-09 20:45:28 -04:00
chat _delay = this . get ( 'ffz_chat_delay' ) ,
2016-06-02 20:04:40 -04:00
delay = Math . max (
2016-08-09 20:45:28 -04:00
( chat _delay !== 0 ? 50 + Math . max ( 0 , ( chat _delay + ( this . ffzPending [ 0 ] . time || 0 ) ) - now ) : 0 ) ,
2015-11-19 02:45:56 -05:00
( f . settings . chat _batching !== 0 ? Math . max ( 0 , f . settings . chat _batching - ( now - ( this . _ffz _last _batch || 0 ) ) ) : 0 ) ) ;
2015-11-01 17:28:19 -05:00
2015-08-14 13:21:57 -04:00
this . _ffz _pending _flush = setTimeout ( this . ffzPendingFlush . bind ( this ) , delay ) ;
}
} ,
2015-08-09 23:32:29 -07:00
ffzPendingFlush : function ( ) {
2015-08-14 13:21:57 -04:00
this . _ffz _pending _flush = null ;
2016-08-09 20:45:28 -04:00
var now = this . _ffz _last _batch = Date . now ( ) ,
2016-09-30 13:09:03 -04:00
chat _delay = this . get ( 'ffz_chat_delay' ) ,
to _display = [ ] ;
2015-11-01 17:28:19 -05:00
2015-08-09 23:32:29 -07:00
for ( var i = 0 , l = this . ffzPending . length ; i < l ; i ++ ) {
var msg = this . ffzPending [ i ] ;
2016-07-13 02:06:50 -04:00
if ( msg . removed ) {
// Don't keep this message ID around.
2016-09-09 17:34:20 -04:00
var msg _id = msg && msg . tags && msg . tags . id ,
notice _type = msg && msg . tags && msg . tags [ 'msg-id' ] ;
2016-07-13 02:06:50 -04:00
if ( msg _id && this . ffz _ids && this . ffz _ids [ msg _id ] )
delete this . ffz _ids [ msg _id ] ;
2016-09-09 17:34:20 -04:00
if ( notice _type && this . ffz _last _notices && this . ffz _last _notices [ notice _type ] === msg )
delete this . ffz _last _notices [ notice _type ] ;
2015-08-14 13:21:57 -04:00
continue ;
2016-07-13 02:06:50 -04:00
}
2015-08-14 13:21:57 -04:00
2016-08-09 20:45:28 -04:00
if ( chat _delay !== 0 && ( chat _delay + msg . time > now ) )
2015-08-14 13:21:57 -04:00
break ;
2016-07-13 02:06:50 -04:00
msg . pending = false ;
2016-09-30 13:09:03 -04:00
to _display . push ( msg ) ;
2015-08-09 23:32:29 -07:00
}
2016-09-30 13:09:03 -04:00
this . ffzPushMessages ( to _display ) ;
2015-08-14 13:21:57 -04:00
this . ffzPending = this . ffzPending . slice ( i ) ;
this . ffzSchedulePendingFlush ( now ) ;
2015-08-09 23:32:29 -07:00
} ,
ffzShouldShowMessage : function ( msg ) {
2015-11-19 02:45:56 -05:00
if ( ! f . settings . hosted _sub _notices && msg . style === 'notification' && HOSTED _SUB . test ( msg . message ) )
return false ;
2015-08-09 23:32:29 -07:00
if ( f . settings . remove _bot _ban _notices && this . ffzRecentlyBanned ) {
var banned = '(' + this . ffzRecentlyBanned . join ( '|' ) + ')' ;
var bots = {
'nightbot' : '^' + banned ,
'moobot' : '\\(' + banned + '\\)' ,
'xanbot' : '^' + banned ,
} ;
if ( msg . from in bots && ( new RegExp ( bots [ msg . from ] ) ) . test ( msg . message ) ) {
return false ;
}
}
return true ;
} ,
2016-05-13 23:56:59 -04:00
ffzShouldDisplayNotice : function ( ) {
return f . settings . timeout _notices === 2 || ( f . settings . timeout _notices === 1 && this . get ( 'isModeratorOrHigher' ) ) ;
} ,
2017-01-18 18:33:56 -05:00
ffzRetokenizeUser : function ( user , max _age , update _badges ) {
2016-12-19 21:33:45 -05:00
// Retokenize all messages by a user, or just all messages.
var messages = this . get ( 'messages' ) ,
i = messages . length ,
now = new Date ;
while ( i -- ) {
var msg = messages [ i ] ,
age = msg . date ? ( now - msg . date ) / 1000 : 0 ;
if ( max _age && age > max _age )
break ;
if ( ! user || msg . from === user ) {
msg . cachedTokens = null ;
2017-01-18 18:33:56 -05:00
if ( msg . _line ) {
2016-12-19 21:33:45 -05:00
Ember . propertyDidChange ( msg . _line , 'ffzTokenizedMessage' ) ;
2017-01-18 18:33:56 -05:00
if ( update _badges )
msg . _line . ffzUpdateBadges ( ) ;
}
2016-12-19 21:33:45 -05:00
}
}
messages = this . ffzPending ;
i = messages ? messages . length : 0 ;
while ( i -- ) {
var msg = messages [ i ] ,
age = msg . date ? ( now - msg . date ) / 1000 : 0 ;
if ( max _age && age > max _age )
break ;
if ( ! user || msg . from === user )
msg . cachedTokens = null ;
}
} ,
2016-05-13 23:56:59 -04:00
addNotification : function ( msg ) {
if ( msg ) {
// We don't want to display these notices because we're injecting our own messages.
if ( ( msg . msgId === 'timeout_success' || msg . msgId === 'ban_success' ) && this . ffzShouldDisplayNotice ( ) )
return ;
2016-09-30 13:09:03 -04:00
// f.log("Notification", msg);
2016-09-09 17:34:20 -04:00
if ( ! msg . tags )
msg . tags = { } ;
if ( ! msg . tags [ 'msg-id' ] )
msg . tags [ 'msg-id' ] = msg . msgId ;
if ( ! msg . style )
msg . style = 'admin' ;
if ( this . ffz _waiting _notices && this . ffz _waiting _notices [ msg . msgId ] ) {
msg . has _owner = true ;
msg . message += ' (By: ' + this . ffz _waiting _notices [ msg . msgId ] + ')' ;
delete this . ffz _waiting _notices [ msg . msgId ] ;
}
return this . addMessage ( msg ) ;
2016-05-13 23:56:59 -04:00
}
} ,
2016-07-13 02:06:50 -04:00
onMessage : function ( msg ) {
// We do our own batching. With blackjack, and hookers. You know what? Forget the batching.
this . addMessage ( msg ) ;
} ,
2016-09-30 13:09:03 -04:00
ffzProcessMessage : function ( msg ) {
2015-07-29 01:03:10 -04:00
if ( msg ) {
2016-09-09 17:34:20 -04:00
var notice _type = msg . tags && msg . tags [ 'msg-id' ] ,
2017-06-11 13:30:37 -04:00
is _resub = notice _type === 'resub' || notice _type === 'sub' ,
2016-07-13 02:06:50 -04:00
room _id = this . get ( 'id' ) ,
msg _id = msg . tags && msg . tags . id ;
2016-06-22 14:23:03 -04:00
// Ignore resubs in other rooms.
if ( is _resub && ! f . settings . hosted _sub _notices && ( msg . tags [ 'room-id' ] != this . get ( 'roomProperties._id' ) || HOSTED _SUB . test ( msg . tags [ 'system-msg' ] ) ) )
return ;
// Split this into two messages if requested.
if ( is _resub && f . settings . old _sub _notices ) {
2016-10-16 15:05:11 -04:00
var message = msg . tags [ 'system-msg' ] ,
months = /for (\d+) months/ . exec ( message ) ;
2016-06-22 14:23:03 -04:00
this . addMessage ( {
style : "notification" ,
from : "twitchnotify" ,
date : msg . date || new Date ,
room : room _id ,
2016-10-16 15:05:11 -04:00
message : message ,
ffz _sub _months : months && parseInt ( months [ 1 ] )
2016-06-22 14:23:03 -04:00
} ) ;
// If there's no message just quit now.
if ( ! msg . message )
return ;
// And delete the system message so it won't render weirdly.
msg . tags [ 'system-msg' ] = '' ;
}
2016-09-30 13:09:03 -04:00
// Fix dates for historical messages.
if ( ! msg . date && msg . tags && msg . tags [ 'tmi-sent-ts' ] ) {
var sent = parseInt ( msg . tags [ 'tmi-sent-ts' ] ) ;
if ( sent && ! isNaN ( sent ) && isFinite ( sent ) )
msg . date = new Date ( sent ) ;
}
2015-07-29 01:03:10 -04:00
var is _whisper = msg . style === 'whisper' ;
2015-11-10 21:37:30 -05:00
// Ignore whispers if conversations are enabled.
2016-09-09 17:34:20 -04:00
if ( is _whisper ) {
var conv _enabled = utils . ember _lookup ( 'controller:application' ) . get ( 'isConversationsEnabled' ) ;
if ( conv _enabled || ( ! conv _enabled && f . settings . hide _whispers _in _embedded _chat ) )
return ;
}
2015-06-10 18:46:04 -04:00
2015-07-29 01:03:10 -04:00
if ( ! is _whisper )
2016-06-22 14:23:03 -04:00
msg . room = room _id ;
2015-07-29 01:03:10 -04:00
2015-08-21 19:00:48 -04:00
// Look up color and labels.
if ( this . tmiRoom && msg . from ) {
if ( ! msg . color )
msg . color = msg . tags && msg . tags . color ? msg . tags . color : this . tmiSession . getColor ( msg . from . toLowerCase ( ) ) ;
if ( ! msg . labels )
msg . labels = this . tmiRoom . getLabels ( msg . from ) ;
}
2016-10-14 20:43:34 -04:00
// Tag the broadcaster.
if ( room _id === msg . from )
msg . tags . mod = true ;
2017-08-16 13:24:55 -04:00
// Are there no emotes?
if ( msg . tags && ! msg . tags . emotes ) {
var user = f . get _user ( ) ,
is _me = user && user . login === msg . from ;
if ( is _me ) {
var user _emotes = utils . ember _lookup ( 'service:user-emotes' ) ;
msg . tags . emotes = tmimotes && user _emotes && tmimotes ( user _emotes . tryParseEmotes ( msg . message ) ) ;
}
}
2017-09-15 14:41:00 -04:00
// Handle Rich Content
if ( commerce && commerce . addCommerceParamsAsRichContent )
commerce . addCommerceParamsAsRichContent ( msg ) ;
/ * v a r f i r s t _ c l i p = C L I P _ U R L . e x e c ( m s g . m e s s a g e ) ;
if ( first _clip ) {
msg . tags . content = msg . tags . content || { } ;
msg . tags . content . clips = msg . tags . content . clips || [ ] ;
msg . tags . content . clips . push ( {
index : first _clip . index ,
removeOriginal : true ,
data : {
url : first _clip [ 0 ] ,
slug : first _clip [ 1 ]
}
} )
} else {
var first _vid = VIDEO _URL . exec ( msg . message ) ;
if ( first _vid ) {
msg . tags . content = msg . tags . content || { } ;
msg . tags . content . clips = msg . tags . content . clips || [ ] ;
msg . tags . content . clips . push ( {
index : first _vid . index ,
removeOriginal : true ,
data : {
is _video : true ,
url : first _vid [ 0 ] ,
video : first _vid [ 1 ]
}
} )
} / * else {
var first _ffz = FFZ _URL . exec ( msg . message ) ;
if ( first _ffz ) {
msg . tags . content = msg . tags . content || { } ;
msg . tags . content . ffz _emotes = msg . tags . content . ffz _emotes || [ ] ;
msg . tags . content . ffz _emotes . push ( {
index : first _ffz . index ,
removeOriginal : true ,
data : {
url : first _ffz [ 0 ] ,
id : first _ffz [ 1 ]
}
} )
}
}
} * /
2015-07-29 01:03:10 -04:00
// Tokenization
f . tokenize _chat _line ( msg , false , this . get ( 'roomProperties.hide_chat_links' ) ) ;
2016-09-30 13:09:03 -04:00
// Check for a new subscription line that would need a chat badge.
2016-07-13 02:31:26 -04:00
if ( msg . from === 'twitchnotify' && msg . message . indexOf ( 'subscribed to' ) === - 1 && msg . message . indexOf ( 'subscribed' ) !== - 1 ) {
if ( ! msg . tags )
msg . tags = { } ;
2016-06-05 22:12:54 -04:00
if ( ! msg . tags . badges )
msg . tags . badges = { } ;
2016-10-16 15:05:11 -04:00
var ffz _room = f . rooms && f . rooms [ room _id ] ,
badges = ffz _room && ffz _room . badges || { } ,
sub _version = badges && badges . subscriber && badges . subscriber . versions ;
2017-08-16 13:24:55 -04:00
if ( sub _version && ! isNaN ( msg . ffz _sub _months ) ) {
2016-10-16 15:05:11 -04:00
var months = msg . ffz _sub _months ;
msg . tags . badges . subscriber = ( months >= 24 && sub _version [ 24 ] ) ? 24 :
( months >= 12 && sub _version [ 12 ] ) ? 12 :
( months >= 6 && sub _version [ 6 ] ) ? 6 :
2016-11-03 21:44:44 -04:00
( months >= 3 && sub _version [ 3 ] ) ? 3 : 0 ;
2016-10-16 15:05:11 -04:00
} else
2016-11-03 21:44:44 -04:00
msg . tags . badges . subscriber = 0 ;
2016-10-16 15:05:11 -04:00
2016-07-13 02:31:26 -04:00
msg . tags . subscriber = true ;
if ( msg . labels && msg . labels . indexOf ( "subscriber" ) === - 1 )
msg . labels . push ( "subscriber" ) ;
}
2016-03-23 19:28:22 -04:00
2016-09-30 13:09:03 -04:00
// Color processing.
if ( msg . color )
f . _handle _color ( msg . color ) ;
return msg ;
}
} ,
addMessage : function ( msg ) {
msg = this . ffzProcessMessage ( msg ) ;
2016-11-13 14:37:19 -05:00
if ( ! msg || msg . ffz _removed )
2016-09-30 13:09:03 -04:00
return ;
var msg _id = msg . tags && msg . tags . id ,
notice _type = msg . tags && msg . tags [ 'msg-id' ] ,
is _whisper = msg . style === 'whisper' ;
// If this message is already in the room, discard the duplicate.
if ( msg _id && this . ffz _ids && this . ffz _ids [ msg _id ] )
return ;
2016-10-05 23:07:10 -04:00
// If it's historical, make sure it's for this room.
if ( msg . tags && msg . tags . historical && msg . tags [ 'room-id' ] != this . get ( 'roomProperties._id' ) )
return ;
2016-06-02 20:04:40 -04:00
2017-08-16 13:24:55 -04:00
// Message Filtering
f . api _trigger ( 'room-message' , msg ) ;
if ( msg . ffz _removed )
return ;
2016-09-30 13:09:03 -04:00
// Keep the history.
2016-10-14 20:43:34 -04:00
if ( ! is _whisper && msg . from && msg . from !== 'jtv' && msg . from !== 'twitchnotify' )
2017-01-30 16:36:33 -05:00
this . addUserHistory ( msg ) ;
2015-07-04 17:06:36 -04:00
2016-09-30 13:09:03 -04:00
// Clear the last ban for that user.
var f _room = f . rooms && f . rooms [ msg . room ] ,
ban _history = f _room && f _room . ban _history ;
2016-05-13 23:56:59 -04:00
2016-09-30 13:09:03 -04:00
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 ;
2016-05-22 14:08:11 -04:00
2016-09-30 13:09:03 -04:00
} else
ban _history [ msg . from ] = false ;
}
2016-05-22 14:08:11 -04:00
2016-05-13 23:56:59 -04:00
2016-09-30 13:09:03 -04:00
// Check for message from us.
if ( ! is _whisper && ! msg . ffz _deleted ) {
var user = f . get _user ( ) ;
if ( user && user . login === msg . from ) {
var was _banned = this . get ( 'ffz_banned' ) ;
this . set ( 'ffz_banned' , false ) ;
2015-07-29 01:03:10 -04:00
2016-09-30 13:09:03 -04:00
// Update the wait time.
if ( this . get ( 'isSubscriber' ) || this . get ( 'isModeratorOrHigher' ) || ! this . get ( 'slowMode' ) )
this . updateWait ( 0 , was _banned )
else if ( this . get ( 'slowMode' ) )
this . updateWait ( this . get ( 'slow' ) ) ;
2015-06-05 03:59:28 -04:00
}
2015-07-29 01:03:10 -04:00
}
2015-07-18 21:10:27 -04:00
2015-07-29 01:03:10 -04:00
2016-07-13 02:06:50 -04:00
// We're past the last return, so store the message
// now that we know we're keeping it.
if ( msg _id ) {
var ids = this . ffz _ids = this . ffz _ids || { } ;
ids [ msg _id ] = msg ;
}
2016-09-09 17:34:20 -04:00
// If this is a notice, store that this is the last of its type.
if ( notice _type ) {
var ids = this . ffz _last _notices = this . ffz _last _notices || { } ;
ids [ notice _type ] = msg ;
}
2016-07-13 02:31:26 -04:00
// Report this message to the dashboard.
2017-06-11 13:30:37 -04:00
if ( msg . from && msg . from !== "jtv" && msg . from !== "twitchnotify" )
this . ffzSafePM ( { from _ffz : true , command : 'chat_message' , data : { from : msg . from , room : msg . room } } , "*" ) ; //location.protocol + "//www.twitch.tv/");
2016-03-23 19:28:22 -04:00
2016-09-30 13:09:03 -04:00
// Flagging for review.
if ( msg . tags && msg . tags . risk === "high" )
msg . flaggedForReview = true ;
// Add the message. We don't do super anymore because it does stupid stuff.'
this . pushMessage ( msg ) ;
msg . from && msg . style !== "admin" && msg . style !== "notification" && msg . tags && this . addChatter ( msg ) ;
this . trackLatency ( msg ) ;
//return this._super(msg);
2015-06-05 03:59:28 -04:00
} ,
setHostMode : function ( e ) {
2015-07-13 21:52:44 -04:00
this . set ( 'ffz_host_target' , e && e . hostTarget || null ) ;
var user = f . get _user ( ) ;
if ( user && f . _cindex && this . get ( 'id' ) === user . login )
f . _cindex . ffzUpdateHostButton ( ) ;
2015-08-14 13:21:57 -04:00
2016-10-01 13:43:08 -04:00
// If hosting is disabled, or this isn't the current channel room,
// ignore the host mode.
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
2015-06-05 03:59:28 -04:00
if ( ! Chat || Chat . get ( 'currentChannelRoom' ) !== this )
return ;
2016-10-01 13:43:08 -04:00
var target = f . settings . hosted _channels ? ( e . hostTarget ? e . hostTarget . toLowerCase ( ) : null ) : null ,
channel = this . get ( "channel" ) ;
if ( channel ) {
var delay = 0 ;
if ( target && ! e . recentlyJoined ) {
var percentile = Math . max ( ( e . numViewers || 0 ) / . 5 , 4000 ) ;
delay = 3000 + Math . floor ( ( percentile || 0 ) * Math . random ( ) ) ;
}
2016-11-23 16:41:49 -05:00
var c = this . get ( "store" ) . peekRecord ( "channel" , channel . get ( "name" ) ) ;
if ( c ) {
if ( target )
this . pendingFetchHostModeTarget = Ember . run . debounce ( this , "fetchHostModeTarget" , {
currentChannel : c ,
targetName : target
} , delay ) ;
else
c . set ( "hostModeTarget" , null ) ;
}
2016-10-01 13:43:08 -04:00
}
2015-01-20 20:25:26 -05:00
} ,
2016-12-14 02:40:18 -05:00
sendTags : function ( text , tags ) {
var tmi = this . get ( 'tmiRoom' ) ,
conn = tmi && tmi . _roomConn ,
tag _string = utils . build _tags ( tags || { } ) ;
if ( conn )
conn . send ( ( tag _string ? '@' + tag _string + ' ' : '' ) + 'PRIVMSG ' + tmi . ircChannel + ' :' + text ) ;
} ,
2016-09-30 13:09:03 -04:00
send : function ( text , ignore _history , used _aliases ) {
2015-02-10 01:34:23 -05:00
try {
2015-12-12 13:28:35 -05:00
this . ffz _last _input = Date . now ( ) ;
2016-06-22 14:23:03 -04:00
var first _char = text . charAt ( 0 ) ,
is _cmd = first _char === '/' || first _char === '.' ;
// Strip trailing whitespace from commands.
if ( is _cmd )
text = text . replace ( /\s+$/ , '' ) ;
2015-10-17 18:05:44 -04:00
if ( text && ! ignore _history ) {
2015-07-06 00:09:21 -04:00
// Command History
var mru = this . get ( 'mru_list' ) ,
ind = mru . indexOf ( text ) ;
2015-08-14 13:21:57 -04:00
2015-07-06 00:09:21 -04:00
if ( ind !== - 1 )
mru . splice ( ind , 1 )
else if ( mru . length > 20 )
mru . pop ( ) ;
2015-08-14 13:21:57 -04:00
2015-07-06 00:09:21 -04:00
mru . unshift ( text ) ;
}
2015-08-14 13:21:57 -04:00
2016-09-30 13:09:03 -04:00
if ( is _cmd ) {
var cmd = text . substr ( 1 ) . split ( ' ' , 1 ) [ 0 ] . toLowerCase ( ) ,
was _handled = false ;
2015-02-10 01:34:23 -05:00
2016-09-30 13:09:03 -04:00
if ( cmd === "ffz" ) {
f . run _ffz _command ( text . substr ( 5 ) , this . get ( 'id' ) ) ;
was _handled = true ;
} else if ( f . _command _aliases [ cmd ] ) {
used _aliases = used _aliases || [ ] ;
if ( used _aliases . indexOf ( cmd ) !== - 1 ) {
f . room _message ( f . rooms [ this . get ( 'id' ) ] , "Error: Your command aliases are recursing. [Path: " + used _aliases . join ( ", " ) + "]" ) ;
was _handled = true ;
} else {
2016-10-12 16:42:43 -04:00
var alias = f . _command _aliases [ cmd ] [ 0 ] ,
2016-09-30 13:09:03 -04:00
args = text . substr ( 1 + cmd . length ) . trimLeft ( ) . split ( /\s+/g ) ,
output = utils . replace _cmd _variables ( alias , null , this , null , args ) ;
used _aliases . push ( cmd ) ;
this . set ( "messageToSend" , "" ) ;
var lines = output . split ( /\s*<LINE>\s*/g ) ;
for ( var i = 0 ; i < lines . length ; i ++ )
this . send ( lines [ i ] , true , used _aliases ) ;
return ;
}
} else if ( f . run _command ( text , this . get ( 'id' ) ) )
was _handled = true ;
if ( was _handled ) {
this . set ( "messageToSend" , "" ) ;
return ;
}
2015-02-10 01:34:23 -05:00
}
} catch ( err ) {
f . error ( "send: " + err ) ;
}
return this . _super ( text ) ;
2015-06-05 03:59:28 -04:00
} ,
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
ffzUpdateUnread : function ( ) {
2016-03-23 20:23:04 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ;
2015-12-12 13:28:35 -05:00
if ( Chat && Chat . get ( 'currentRoom' ) === this )
this . resetUnreadCount ( ) ;
else if ( f . _chatv )
f . _chatv . ffzUpdateUnread ( this . get ( 'id' ) ) ;
2015-06-05 03:59:28 -04:00
} . observes ( 'unreadCount' ) ,
ffzInitChatterCount : function ( ) {
2017-06-11 13:30:37 -04:00
if ( ! this . tmiRoom || ! f . settings . chatter _count ) {
this . set ( "ffz_chatters" , [ ] ) ;
this . ffzUpdateChatters ( ) ;
2015-06-05 03:59:28 -04:00
return ;
2017-06-11 13:30:37 -04:00
}
2015-06-05 03:59:28 -04:00
2015-07-29 01:03:10 -04:00
if ( this . _ffz _chatter _timer ) {
clearTimeout ( this . _ffz _chatter _timer ) ;
this . _ffz _chatter _timer = undefined ;
}
2015-06-05 03:59:28 -04:00
var room = this ;
this . tmiRoom . list ( ) . done ( function ( data ) {
2017-06-11 13:30:37 -04:00
var chatters = [ ] ;
2015-06-05 03:59:28 -04:00
data = data . data . chatters ;
2015-08-28 22:54:12 -04:00
if ( data && data . admins )
for ( var i = 0 ; i < data . admins . length ; i ++ )
2017-06-11 13:30:37 -04:00
chatters . push ( data . admins [ i ] ) ;
2015-08-28 22:54:12 -04:00
if ( data && data . global _mods )
for ( var i = 0 ; i < data . global _mods . length ; i ++ )
2017-06-11 13:30:37 -04:00
chatters . push ( data . global _mods [ i ] ) ;
2015-08-28 22:54:12 -04:00
if ( data && data . moderators )
for ( var i = 0 ; i < data . moderators . length ; i ++ )
2017-06-11 13:30:37 -04:00
chatters . push ( data . moderators [ i ] ) ;
2015-08-28 22:54:12 -04:00
if ( data && data . staff )
for ( var i = 0 ; i < data . staff . length ; i ++ )
2017-06-11 13:30:37 -04:00
chatters . push ( data . staff [ i ] ) ;
2015-08-28 22:54:12 -04:00
if ( data && data . viewers )
for ( var i = 0 ; i < data . viewers . length ; i ++ )
2017-06-11 13:30:37 -04:00
chatters . push ( data . viewers [ i ] ) ;
2015-06-05 03:59:28 -04:00
room . set ( "ffz_chatters" , chatters ) ;
room . ffzUpdateChatters ( ) ;
2017-06-11 13:30:37 -04:00
2015-07-29 01:03:10 -04:00
} ) . always ( function ( ) {
room . _ffz _chatter _timer = setTimeout ( room . ffzInitChatterCount . bind ( room ) , 300000 ) ;
2015-06-05 03:59:28 -04:00
} ) ;
} ,
2015-01-20 01:53:18 -05:00
2015-07-04 17:06:36 -04:00
2015-06-05 03:59:28 -04:00
ffzUpdateChatters : function ( add , remove ) {
if ( ! f . settings . chatter _count )
return ;
2015-01-20 01:53:18 -05:00
2017-06-11 13:30:37 -04:00
var chatters = this . get ( "ffz_chatters" ) || [ ] ;
if ( add && chatters . indexOf ( add ) === - 1 )
chatters . push ( add ) ;
if ( remove ) {
var ind = chatters . indexOf ( remove ) ;
if ( ind !== - 1 )
chatters . splice ( ind , 1 ) ;
}
2015-06-05 03:59:28 -04:00
if ( f . _cindex )
2016-10-13 23:05:54 -04:00
f . _cindex . ffzUpdateMetadata ( 'chatters' ) ;
2015-01-20 01:53:18 -05:00
2017-06-11 13:30:37 -04:00
this . ffzSafePM ( { from _ffz : true , command : 'chatter_count' , data : { room : this . get ( 'id' ) , chatters : ( this . get ( 'ffz_chatters' ) || [ ] ) . length } } , "*" ) ; //location.protocol + "//www.twitch.tv/");
2015-06-05 03:59:28 -04:00
} ,
2015-01-20 01:53:18 -05:00
2017-06-11 13:30:37 -04:00
ffzSafePM : function ( message , origin ) {
try {
if ( window !== window . parent && parent . postMessage )
return parent . postMessage ( message , origin ) ;
} catch ( err ) { }
} ,
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
ffzPatchTMI : function ( ) {
2016-05-13 23:56:59 -04:00
var tmi = this . get ( 'tmiRoom' ) ,
room = this ;
2016-05-20 17:30:34 -04:00
if ( this . get ( 'ffz_is_patched' ) || ! tmi )
2015-06-05 03:59:28 -04:00
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( f . settings . chatter _count )
this . ffzInitChatterCount ( ) ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
// Let's get chatter information!
2015-07-04 17:06:36 -04:00
// TODO: Remove this cause it's terrible.
2015-06-05 03:59:28 -04:00
var connection = tmi . _roomConn . _connection ;
if ( ! connection . ffz _cap _patched ) {
connection . ffz _cap _patched = true ;
connection . _send ( "CAP REQ :twitch.tv/membership" ) ;
connection . on ( "opened" , function ( ) {
this . _send ( "CAP REQ :twitch.tv/membership" ) ;
} , connection ) ;
2015-07-04 17:06:36 -04:00
}
2015-06-05 03:59:28 -04:00
2015-07-04 17:06:36 -04:00
// NOTICE for catching slow-mode updates
tmi . on ( 'notice' , function ( msg ) {
if ( msg . msgId === 'msg_slowmode' ) {
var match = /in (\d+) seconds/ . exec ( msg . message ) ;
if ( match ) {
room . updateWait ( parseInt ( match [ 1 ] ) ) ;
}
2015-06-05 03:59:28 -04:00
}
2015-07-04 17:06:36 -04:00
if ( msg . msgId === 'msg_timedout' ) {
var match = /for (\d+) more seconds/ . exec ( msg . message ) ;
if ( match ) {
room . set ( 'ffz_banned' , true ) ;
room . updateWait ( parseInt ( match [ 1 ] ) ) ;
}
}
if ( msg . msgId === 'msg_banned' ) {
room . set ( 'ffz_banned' , true ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
2015-07-13 21:52:44 -04:00
if ( msg . msgId === 'hosts_remaining' ) {
var match = /(\d+) host command/ . exec ( msg . message ) ;
if ( match ) {
room . set ( 'ffz_hosts_left' , parseInt ( match [ 1 ] || 0 ) ) ;
f . _cindex && f . _cindex . ffzUpdateHostButton ( ) ;
}
}
2015-07-04 17:06:36 -04:00
} ) ;
2015-06-05 03:59:28 -04:00
// Check this shit.
tmi . _roomConn . _connection . off ( "message" , tmi . _roomConn . _onIrcMessage , tmi . _roomConn ) ;
tmi . _roomConn . _onIrcMessage = function ( ircMsg ) {
if ( ircMsg . target != this . ircChannel )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
switch ( ircMsg . command ) {
case "JOIN" :
if ( this . _session && this . _session . nickname === ircMsg . sender ) {
this . _onIrcJoin ( ircMsg ) ;
} else
f . settings . chatter _count && room . ffzUpdateChatters ( ircMsg . sender ) ;
break ;
case "PART" :
if ( this . _session && this . _session . nickname === ircMsg . sender ) {
this . _resetActiveState ( ) ;
this . _connection . _exitedRoomConn ( ) ;
this . _trigger ( "exited" ) ;
} else
f . settings . chatter _count && room . ffzUpdateChatters ( null , ircMsg . sender ) ;
break ;
default :
break ;
}
}
tmi . _roomConn . _connection . on ( "message" , tmi . _roomConn . _onIrcMessage , tmi . _roomConn ) ;
this . set ( 'ffz_is_patched' , true ) ;
2015-07-29 01:03:10 -04:00
} . observes ( 'tmiRoom' ) ,
// Room State Stuff
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
onSlowOff : function ( ) {
if ( ! this . get ( 'slowMode' ) )
this . updateWait ( 0 ) ;
} . observes ( 'slowMode' )
2015-06-05 03:59:28 -04:00
} ) ;
2015-01-12 17:58:07 -05:00
}