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' ) ,
2015-11-07 22:56:15 -05:00
helpers ,
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' ,
'unhost' : 'host_off'
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-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
is _android = navigator . userAgent . indexOf ( 'Android' ) !== - 1 ,
2015-01-20 01:53:18 -05:00
moderator _css = function ( room ) {
if ( ! room . moderator _badge )
return "" ;
2016-09-30 13:09:03 -04:00
return '.from-display-preview[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement),.chat-line[data-room="' + room . id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room . moderator _badge + '") !important; }' ;
2015-11-07 22:56:15 -05:00
} ;
try {
helpers = window . require && window . require ( "ember-twitch-chat/helpers/chat-line-helpers" ) ;
} catch ( err ) { }
2015-01-20 01:53:18 -05:00
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _room = function ( ) {
this . rooms = { } ;
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 ,
2015-12-12 13:28:35 -05:00
profileHref : Twitch . uri . profile ( e . sender ) ,
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 ) ;
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
this . log ( "Hooking the Ember Room view." ) ;
2016-07-13 02:06:50 -04:00
this . update _views ( 'view:room' , this . modify _room _view ) ;
2015-07-04 17:06:36 -04:00
}
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 ++ ) {
ps . Unlisten ( {
topic : old _topics [ i ] ,
success : function ( ) { } ,
2016-09-30 13:09:03 -04:00
failure : function ( t ) { f . log ( "[PubSub] Failed to unlisten to topic: " + old _topics [ i ] , t ) ; }
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 ( ) ,
token = user . chat _oauth _token ;
for ( var i = 0 ; i < pubsub . chatTopics . length ; i ++ ) {
ps . Unlisten ( {
topic : pubsub . chatTopics [ i ] ,
success : function ( ) { } ,
2016-09-30 13:09:03 -04:00
failure : function ( t ) { f . log ( "[PubSub] Failed to unlisten to topic: " + old _topics [ i ] , t ) ; }
2016-09-09 17:34:20 -04:00
} ) ;
ps . Listen ( {
topic : pubsub . chatTopics [ 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 ( pubsub , pubsub . _onPubsubMessage , pubsub . chatTopics [ i ] )
} ) ;
}
} ) ;
}
2015-07-04 17:06:36 -04:00
// --------------------
// View Customization
// --------------------
2016-07-13 02:06:50 -04:00
FFZ . prototype . modify _room _view = function ( view ) {
2015-07-04 17:06:36 -04:00
var f = this ;
2016-07-13 02:06:50 -04:00
utils . ember _reopen _view ( view , {
ffz _init : function ( ) {
2015-07-04 17:06:36 -04:00
f . _roomv = this ;
this . ffz _frozen = false ;
2016-06-22 14:23:03 -04:00
this . ffz _ctrl = false ;
// Monitor the Ctrl key.
this . _ffz _keyw = this . ffzOnKey . bind ( this ) ;
document . body . addEventListener ( 'keydown' , this . _ffz _keyw ) ;
document . body . addEventListener ( 'keyup' , this . _ffz _keyw ) ;
2015-07-04 17:06:36 -04:00
2015-07-18 21:10:27 -04:00
// Fix scrolling.
this . _ffz _mouse _down = this . ffzMouseDown . bind ( this ) ;
if ( is _android )
// We don't unbind scroll because that messes with the scrollbar. ;_;
this . _$chatMessagesScroller . bind ( 'scroll' , this . _ffz _mouse _down ) ;
this . _$chatMessagesScroller . unbind ( 'mousedown' ) ;
this . _$chatMessagesScroller . bind ( 'mousedown' , this . _ffz _mouse _down ) ;
2015-07-04 17:06:36 -04:00
if ( f . settings . chat _hover _pause )
this . ffzEnableFreeze ( ) ;
if ( f . settings . room _status )
this . ffzUpdateStatus ( ) ;
var controller = this . get ( 'controller' ) ;
if ( controller ) {
controller . reopen ( {
2016-07-13 02:06:50 -04:00
calcRecipientEligibility : function ( e ) {
// Because this doesn't work properly with multiple channel rooms
// by default, do it ourselves.
2016-08-09 20:45:28 -04:00
if ( controller . get ( 'model.isGroupRoom' ) ) {
controller . set ( 'isRecipientBitsIneligible' , true ) ;
controller . set ( 'isBitsHelperShown' , false ) ;
controller . set ( 'minimumBits' , 0 ) ;
controller . set ( 'isBitsTooltipActive' , false ) ;
return ;
}
2016-07-13 02:06:50 -04:00
var id = controller . get ( 'model.roomProperties._id' ) ,
update = function ( data ) {
if ( controller . isDestroyed || controller . get ( 'model.roomProperties._id' ) !== id )
return ;
controller . set ( 'model._ffz_bits_eligibility' , data ) ;
controller . set ( 'isRecipientBitsIneligible' , ! data . eligible ) ;
controller . set ( 'isBitsHelperShown' , data . eligible ) ;
controller . set ( 'minimumBits' , data . minBits ) ;
if ( ! data . eligible )
controller . set ( 'isBitsTooltipActive' , false ) ;
} ;
if ( id === undefined )
return ;
var data = controller . get ( 'model._ffz_bits_eligibility' ) ;
if ( data === undefined )
controller . get ( 'bits' ) . loadRecipientEligibility ( id ) . then ( update ) ;
else
update ( data ) ;
} ,
2015-07-04 17:06:36 -04:00
submitButtonText : function ( ) {
if ( this . get ( "model.isWhisperMessage" ) && this . get ( "model.isWhispersEnabled" ) )
return i18n ( "Whisper" ) ;
var wait = this . get ( "model.slowWait" ) ,
msg = this . get ( "model.messageToSend" ) || "" ;
if ( ( msg . charAt ( 0 ) === "/" && msg . substr ( 0 , 4 ) !== "/me " ) || ! wait || ! f . settings . room _status )
return i18n ( "Chat" ) ;
return utils . time _to _string ( wait , false , false , true ) ;
} . property ( "model.isWhisperMessage" , "model.isWhispersEnabled" , "model.slowWait" )
} ) ;
Ember . propertyDidChange ( controller , 'submitButtonText' ) ;
}
} ,
2016-07-13 02:06:50 -04:00
ffz _destroy : function ( ) {
2015-07-04 17:06:36 -04:00
if ( f . _roomv === this )
f . _roomv = undefined ;
2015-11-14 23:52:49 -05:00
if ( this . _ffz _chat _display )
this . _ffz _chat _display = undefined ;
2016-06-22 14:23:03 -04:00
if ( this . _ffz _keyw ) {
document . body . removeEventListener ( 'keydown' , this . _ffz _keyw ) ;
document . body . removeEventListener ( 'keyup' , this . _ffz _keyw ) ;
this . _ffz _keyw = undefined ;
}
2015-07-04 17:06:36 -04:00
this . ffzDisableFreeze ( ) ;
} ,
2016-09-09 17:34:20 -04:00
2016-06-22 14:23:03 -04:00
ffzOnKey : function ( event ) {
this . ffz _ctrl = event . ctrlKey ;
this . ffz _alt = event . altKey ;
this . ffz _shift = event . shiftKey ;
this . ffz _meta = event . metaKey ;
var cmi = f . settings . chat _mod _icon _visibility ;
if ( ! this . _ffz _outside && cmi > 1 )
this . get ( 'element' ) . classList . toggle ( 'show-mod-icons' ,
cmi === 2 ? this . ffz _ctrl :
cmi === 3 ? this . ffz _meta :
cmi === 4 ? this . ffz _alt :
this . ffz _shift ) ;
if ( this . _ffz _outside || f . settings . chat _hover _pause < 2 )
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 ( ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzUpdateStatus : function ( ) {
var room = this . get ( 'controller.model' ) ,
el = this . get ( 'element' ) ,
cont = el && el . querySelector ( '.chat-buttons-container' ) ;
if ( ! cont )
2015-07-13 21:52:44 -04:00
return ;
2015-07-04 17:06:36 -04:00
2016-05-06 02:23:12 -04:00
var btn = cont . querySelector ( 'button' ) ;
2015-07-04 17:06:36 -04:00
if ( f . has _bttv || ! f . settings . room _status ) {
2016-05-06 02:23:12 -04:00
jQuery ( ".ffz.room-state" , cont ) . remove ( ) ;
2015-07-04 17:06:36 -04:00
if ( btn )
btn . classList . remove ( 'ffz-waiting' ) ;
return ;
2016-05-06 02:23:12 -04:00
} else if ( btn ) {
btn . classList . toggle ( 'ffz-waiting' , ( room && room . get ( 'slowWait' ) || 0 ) ) ;
2016-05-12 00:11:50 -04:00
btn . classList . toggle ( 'ffz-banned' , ( room && room . get ( 'ffz_banned' ) || false ) ) ;
2015-07-04 17:06:36 -04:00
}
2016-07-13 02:06:50 -04:00
var badge , id , info , vis _count = 0 , label ;
2016-05-06 02:23:12 -04:00
for ( var i = 0 ; i < STATUS _BADGES . length ; i ++ ) {
info = STATUS _BADGES [ i ] ;
id = 'ffz-stat-' + info [ 0 ] ;
badge = cont . querySelector ( '#' + id ) ;
visible = typeof info [ 1 ] === "function" ? info [ 1 ] . call ( f , room ) : room && room . get ( info [ 1 ] ) ;
2016-05-20 17:30:34 -04:00
if ( typeof visible === "string" )
visible = visible === "1" ;
2016-07-13 02:06:50 -04:00
label = typeof info [ 3 ] === "function" ? info [ 3 ] . call ( f , room ) : undefined ;
2016-05-06 02:23:12 -04:00
if ( ! badge ) {
2016-07-13 02:06:50 -04:00
badge = utils . createElement ( 'span' , 'ffz room-state stat float-right' , ( label || info [ 0 ] ) . charAt ( 0 ) . toUpperCase ( ) + '<span>' + ( label || info [ 0 ] ) . substr ( 1 ) . toUpperCase ( ) + '</span>' ) ;
2016-05-06 02:23:12 -04:00
badge . id = id ;
2016-09-30 13:09:03 -04:00
jQuery ( badge ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'se' ) } ) ;
2016-05-06 02:23:12 -04:00
cont . appendChild ( badge ) ;
}
2015-08-09 23:32:29 -07:00
2016-07-13 02:06:50 -04:00
if ( label )
badge . innerHTML = ( label || info [ 0 ] ) . charAt ( 0 ) . toUpperCase ( ) + '<span>' + ( label || info [ 0 ] ) . substr ( 1 ) . toUpperCase ( ) + '</span>' ;
2016-05-06 02:23:12 -04:00
badge . title = typeof info [ 2 ] === "function" ? info [ 2 ] . call ( f , room ) : info [ 2 ] ;
badge . classList . toggle ( 'hidden' , ! visible ) ;
2016-09-30 13:09:03 -04:00
badge . classList . toggle ( 'faded' , info [ 4 ] !== undefined ? typeof info [ 4 ] === "function" ? info [ 4 ] . call ( f , room ) : info [ 4 ] : false ) ;
2016-05-06 02:23:12 -04:00
if ( visible )
vis _count ++ ;
2015-11-01 17:28:19 -05:00
}
2015-08-14 13:21:57 -04:00
2016-05-06 02:23:12 -04:00
jQuery ( ".ffz.room-state" , cont ) . toggleClass ( "truncated" , vis _count > 3 ) ;
2015-07-04 17:06:36 -04:00
} . observes ( 'controller.model' ) ,
ffzEnableFreeze : function ( ) {
var el = this . get ( 'element' ) ,
messages = el . querySelector ( '.chat-messages' ) ;
if ( ! messages )
return ;
this . _ffz _messages = messages ;
2016-06-22 14:23:03 -04:00
this . _ffz _interval = setInterval ( this . ffzPulse . bind ( this ) , 200 ) ;
2015-08-14 13:21:57 -04:00
2015-07-04 17:06:36 -04:00
this . _ffz _mouse _move = this . ffzMouseMove . bind ( this ) ;
this . _ffz _mouse _out = this . ffzMouseOut . bind ( this ) ;
messages . addEventListener ( 'mousemove' , this . _ffz _mouse _move ) ;
2015-07-18 21:10:27 -04:00
messages . addEventListener ( 'touchmove' , this . _ffz _mouse _move ) ;
2015-07-04 17:06:36 -04:00
messages . addEventListener ( 'mouseout' , this . _ffz _mouse _out ) ;
} ,
ffzDisableFreeze : function ( ) {
if ( this . _ffz _interval ) {
clearInterval ( this . _ffz _interval ) ;
this . _ffz _interval = undefined ;
}
this . ffzUnfreeze ( ) ;
var messages = this . _ffz _messages ;
if ( ! messages )
return ;
this . _ffz _messages = undefined ;
if ( this . _ffz _mouse _move ) {
messages . removeEventListener ( 'mousemove' , this . _ffz _mouse _move ) ;
2016-07-13 02:31:26 -04:00
messages . removeEventListener ( 'touchmove' , this . _ffz _mouse _move ) ;
2015-07-04 17:06:36 -04:00
this . _ffz _mouse _move = undefined ;
}
if ( this . _ffz _mouse _out ) {
messages . removeEventListener ( 'mouseout' , this . _ffz _mouse _out ) ;
this . _ffz _mouse _out = undefined ;
}
} ,
ffzPulse : function ( ) {
2016-06-22 14:23:03 -04:00
if ( this . ffz _frozen && ! this . ffzShouldBeFrozen ( ) )
this . ffzUnfreeze ( ) ;
2015-07-04 17:06:36 -04:00
} ,
2015-11-14 23:52:49 -05:00
ffzUnfreeze : function ( from _stuck ) {
2015-07-04 17:06:36 -04:00
this . ffz _frozen = false ;
this . _ffz _last _move = 0 ;
this . ffzUnwarnPaused ( ) ;
2015-11-14 23:52:49 -05:00
if ( ! from _stuck && this . get ( 'stuckToBottom' ) )
2015-07-04 17:06:36 -04:00
this . _scrollToBottom ( ) ;
} ,
2016-06-22 14:23:03 -04:00
ffzFreeze : function ( ) {
this . ffz _frozen = true ;
if ( this . get ( 'stuckToBottom' ) ) {
this . set ( 'controller.model.messageBufferSize' , f . settings . scrollback _length + 150 ) ;
this . ffzWarnPaused ( ) ;
}
} ,
2015-07-13 21:52:44 -04:00
ffzMouseDown : function ( event ) {
var t = this . _$chatMessagesScroller ;
2015-07-29 01:03:10 -04:00
if ( t && t [ 0 ] && ( ( ! this . ffz _frozen && "mousedown" === event . type ) || "mousewheel" === event . type || ( is _android && "scroll" === event . type ) ) ) {
2015-07-13 21:52:44 -04:00
var r = t [ 0 ] . scrollHeight - t [ 0 ] . scrollTop - t [ 0 ] . offsetHeight ;
this . _setStuckToBottom ( 10 >= r ) ;
}
} ,
2015-07-04 17:06:36 -04:00
ffzMouseOut : function ( event ) {
this . _ffz _outside = true ;
var e = this ;
2015-07-06 00:09:21 -04:00
setTimeout ( function ( ) {
2016-06-22 14:23:03 -04:00
if ( e . _ffz _outside ) {
if ( f . settings . chat _mod _icon _visibility > 1 )
e . get ( 'element' ) . classList . toggle ( 'show-mod-icons' , false ) ;
2015-07-04 17:06:36 -04:00
e . ffzUnfreeze ( ) ;
2016-06-22 14:23:03 -04:00
}
2015-07-06 00:09:21 -04:00
} , 25 ) ;
2015-07-04 17:06:36 -04:00
} ,
2016-06-22 14:23:03 -04:00
ffzShouldBeFrozen : function ( since ) {
if ( since === undefined )
since = Date . now ( ) - this . _ffz _last _move ;
var hp = f . settings . chat _hover _pause ;
return ( this . ffz _ctrl && ( hp === 2 || hp === 6 ) ) || ( this . ffz _meta && ( hp === 3 || hp === 7 ) ) || ( this . ffz _alt && ( hp === 4 || hp === 8 ) ) || ( this . ffz _shift && ( hp === 5 || hp === 9 ) ) || ( since < 750 && ( hp === 1 || hp > 5 ) ) ;
} ,
2015-07-04 17:06:36 -04:00
ffzMouseMove : function ( event ) {
2016-06-22 14:23:03 -04:00
// Store the last move time.
2015-07-04 17:06:36 -04:00
this . _ffz _last _move = Date . now ( ) ;
this . _ffz _outside = false ;
2016-06-22 14:23:03 -04:00
// If nothing of interest has happened, stop.
if ( event . altKey === this . ffz _alt && event . shiftKey === this . ffz _shift && event . ctrlKey === this . ffz _ctrl && event . metaKey === this . ffz _meta && event . screenX === this . _ffz _last _screenx && event . screenY === this . _ffz _last _screeny )
2015-07-04 17:06:36 -04:00
return ;
2016-06-22 14:23:03 -04:00
// Grab a bit of state.
this . ffz _ctrl = event . ctrlKey ;
this . ffz _alt = event . altKey ;
this . ffz _shift = event . shiftKey ;
this . ffz _meta = event . metaKey ;
2015-07-04 17:06:36 -04:00
this . _ffz _last _screenx = event . screenX ;
this . _ffz _last _screeny = event . screenY ;
2016-06-22 14:23:03 -04:00
var cmi = f . settings . chat _mod _icon _visibility ;
if ( ! this . _ffz _outside && cmi > 1 )
this . get ( 'element' ) . classList . toggle ( 'show-mod-icons' ,
cmi === 2 ? this . ffz _ctrl :
cmi === 3 ? this . ffz _meta :
cmi === 4 ? this . ffz _alt :
this . ffz _shift ) ;
// Should the state have changed?
var should _freeze = this . ffzShouldBeFrozen ( ) ,
freeze _change = this . ffz _frozen !== should _freeze ;
if ( freeze _change )
if ( should _freeze )
this . ffzFreeze ( ) ;
else
this . ffzUnfreeze ( ) ;
2015-07-04 17:06:36 -04:00
} ,
_scrollToBottom : _ . throttle ( function ( ) {
var e = this ,
s = this . _$chatMessagesScroller ;
2016-09-30 13:09:03 -04:00
//this.runTask(function() {
Ember . run . next ( function ( ) {
2016-06-02 20:04:40 -04:00
// Trying random performance tweaks for fun and profit!
( window . requestAnimationFrame || setTimeout ) ( function ( ) {
2015-07-29 01:03:10 -04:00
if ( e . ffz _frozen || ! s || ! s . length )
return ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
s [ 0 ] . scrollTop = s [ 0 ] . scrollHeight ;
e . _setStuckToBottom ( true ) ;
2016-09-30 13:09:03 -04:00
e . _tagVisibleMessages ( ) ;
2015-07-04 17:06:36 -04:00
} )
} )
} , 200 ) ,
_setStuckToBottom : function ( val ) {
this . set ( "stuckToBottom" , val ) ;
2016-09-30 13:09:03 -04:00
var model = this . get ( "controller.model" ) ;
if ( model )
model . messageBufferSize = f . settings . scrollback _length + ( val ? 0 : 150 ) ;
2015-07-13 21:52:44 -04:00
if ( ! val )
2015-11-14 23:52:49 -05:00
this . ffzUnfreeze ( true ) ;
2015-07-04 17:06:36 -04:00
} ,
// Warnings~!
ffzWarnPaused : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( ! el )
return ;
if ( ! warning ) {
warning = document . createElement ( 'div' ) ;
warning . className = 'more-messages-indicator ffz-freeze-indicator' ;
2016-06-22 14:23:03 -04:00
var hp = f . 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 + ')' ;
2015-07-04 17:06:36 -04:00
var cont = el . querySelector ( '.chat-interface' ) ;
if ( ! cont )
return ;
cont . insertBefore ( warning , cont . childNodes [ 0 ] )
}
warning . classList . remove ( 'hidden' ) ;
} ,
ffzUnwarnPaused : function ( ) {
var el = this . get ( 'element' ) ,
warning = el && el . querySelector ( '.chat-interface .more-messages-indicator.ffz-freeze-indicator' ) ;
if ( warning )
warning . classList . add ( 'hidden' ) ;
}
} ) ;
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 ) {
if ( this . has _bttv ) {
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 ) {
this . error ( 'command "' + cmd + '" enabled: ' + err ) ;
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
} ;
2015-01-20 01:53:18 -05:00
FFZ . prototype . add _room = function ( id , room ) {
if ( this . rooms [ id ] )
return this . log ( "Tried to add existing room: " + id ) ;
this . log ( "Adding Room: " + id ) ;
// Create a basic data table for this room.
2015-10-24 22:44:00 -04:00
var data = this . rooms [ id ] = { id : id , room : room , sets : [ ] , ext _sets : [ ] , css : null , needs _history : false } ;
2015-01-20 01:53:18 -05:00
2015-07-04 17:06:36 -04:00
if ( this . follow _sets && this . follow _sets [ id ] ) {
data . extra _sets = this . follow _sets [ id ] ;
delete this . follow _sets [ id ] ;
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 ) {
if ( set . users . indexOf ( id ) === - 1 )
set . users . push ( id ) ;
continue ;
}
this . load _set ( sid , function ( success , data ) {
if ( success )
data . users . push ( id ) ;
} ) ;
}
}
2016-10-05 01:31:10 -04:00
// Look up if the room has moderation logs.
var f = this ;
this . ws _send ( "has_logs" , id , function ( success , response ) {
if ( ! success )
return ;
data . has _logs = response . has _logs ;
data . log _source = response . log _source ;
} , true ) ;
2016-05-22 14:08:11 -04:00
// Is the room important?
this . update _room _important ( id ) ;
if ( data . important ) {
// Let the server know where we are.
this . ws _sub ( "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?
room . set ( 'messageBufferSize' , this . settings . scrollback _length + ( ( this . _roomv && ! this . _roomv . get ( 'stuckToBottom' ) && this . _roomv . get ( 'controller.model.id' ) === id ) ? 150 : 0 ) ) ;
2015-10-24 22:44:00 -04:00
// Load the room's data from the API.
2015-06-05 03:59:28 -04:00
this . load _room ( id ) ;
2015-10-24 22:44:00 -04:00
// Announce this room to any extension callback functions.
for ( var api _id in this . _apis ) {
var api = this . _apis [ api _id ] ;
api . _room _callbacks ( id , data ) ;
}
2015-01-20 01:53:18 -05:00
}
FFZ . prototype . remove _room = function ( id ) {
var room = this . rooms [ id ] ;
if ( ! room )
return ;
this . log ( "Removing Room: " + id ) ;
// Remove the CSS
if ( room . css || room . moderator _badge )
utils . update _css ( this . _room _style , id , null ) ;
// Let the server know we're gone and delete our data for this room.
2016-05-22 14:08:11 -04:00
this . ws _unsub ( "room." + id ) ;
2015-01-20 01:53:18 -05:00
delete this . rooms [ id ] ;
// Clean up sets we aren't using any longer.
2015-06-05 03:59:28 -04:00
if ( id . charAt ( 0 ) === "_" )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
var set = this . emote _sets [ room . set ] ;
if ( set ) {
2015-01-20 01:53:18 -05:00
set . users . removeObject ( 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
}
}
2015-06-10 18:46:04 -04:00
// --------------------
// Chat History
// --------------------
2016-09-30 13:09:03 -04:00
/ * F F Z . p r o t o t y p e . _ l o a d _ h i s t o r y = f u n c t i o n ( r o o m _ i d , s u c c e s s , d a t a ) {
2015-06-10 18:46:04 -04:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
if ( success )
this . log ( "Received " + data . length + " old messages for: " + room _id ) ;
else
return this . log ( "Error retrieving chat history for: " + room _id ) ;
if ( ! data . length )
return ;
2015-11-19 02:45:56 -05:00
return this . _insert _history ( room _id , data , true ) ;
2016-09-30 13:09:03 -04:00
} * /
2015-06-10 18:46:04 -04:00
2016-10-05 01:31:10 -04:00
/ * F F Z . p r o t o t y p e . _ s h o w _ d e l e t e d = f u n c t i o n ( r o o m _ i d ) {
2015-06-10 18:46:04 -04:00
var room = this . rooms [ room _id ] ;
if ( ! room || ! room . room )
return ;
var old _messages = room . room . get ( 'messages.0.ffz_old_messages' ) ;
if ( ! old _messages || ! old _messages . length )
return ;
room . room . set ( 'messages.0.ffz_old_messages' , undefined ) ;
this . _insert _history ( room _id , old _messages ) ;
}
2015-11-19 02:45:56 -05:00
FFZ . prototype . _insert _history = function ( room _id , data , from _server ) {
var room = this . rooms [ room _id ] , f = this ;
2015-06-10 18:46:04 -04:00
if ( ! room || ! room . room )
return ;
2016-05-13 23:56:59 -04:00
var current _user = this . get _user ( ) ,
r = room . room ,
2015-06-10 18:46:04 -04:00
messages = r . get ( 'messages' ) ,
2015-11-19 02:45:56 -05:00
buffer _size = r . get ( 'messageBufferSize' ) ,
2015-06-10 18:46:04 -04:00
tmiSession = r . tmiSession || ( TMI . _sessions && TMI . _sessions [ 0 ] ) ,
2015-11-19 02:45:56 -05:00
delete _links = r . get ( 'roomProperties.hide_chat_links' ) ,
2015-06-10 18:46:04 -04:00
2015-11-14 23:52:49 -05:00
removed = 0 ,
2015-06-10 18:46:04 -04:00
inserted = 0 ,
2015-11-19 02:45:56 -05:00
first _inserted ,
first _existing ,
before ;
2015-06-10 18:46:04 -04:00
2015-11-19 02:45:56 -05:00
first _existing = messages . length ? messages [ 0 ] : null ;
if ( first _existing && first _existing . from === 'jtv' && first _existing . message === 'Welcome to the chat room!' )
first _existing = messages . length > 1 ? messages [ 1 ] : null ;
2015-11-07 22:56:15 -05:00
2015-11-19 02:45:56 -05:00
if ( first _existing )
before = first _existing . date && first _existing . date . getTime ( ) ;
2015-06-10 18:46:04 -04:00
2015-11-19 02:45:56 -05:00
2016-07-13 02:06:50 -04:00
this . parse _history ( data , null , null , room _id , delete _links , tmiSession , function ( msg ) {
2015-11-19 02:45:56 -05:00
if ( from _server )
msg . from _server = true ;
// Skip messages that are from the future.
if ( ! msg . date || ( before && ( before - ( msg . from _server && ! first _existing . from _server ? f . _ws _server _offset || 0 : 0 ) ) < msg . date . getTime ( ) ) )
return true ;
if ( f . settings . remove _deleted && msg . deleted )
return true ;
2016-05-25 01:06:27 -04:00
if ( msg . tags && msg . tags . target && msg . tags . target !== '@@' ) {
2016-05-13 23:56:59 -04:00
var is _mine = current _user && current _user . login === msg . tags . target ;
if ( ! is _mine && ! r . ffzShouldDisplayNotice ( ) )
return true ;
// Display the Ban Reason if we're a moderator or that user.
2016-07-13 02:06:50 -04:00
if ( msg . tags [ 'ban-reason' ] && ( is _mine || r . get ( 'isModeratorOrHigher' ) ) ) {
2016-05-13 23:56:59 -04:00
msg . message = msg . message . substr ( 0 , msg . message . length - 1 ) + ' with reason: ' + msg . tags [ 'ban-reason' ] ;
msg . cachedTokens = [ utils . sanitize ( msg . message ) ] ;
}
}
2015-11-19 02:45:56 -05:00
if ( r . shouldShowMessage ( msg ) && r . ffzShouldShowMessage ( msg ) ) {
if ( messages . length < buffer _size ) {
2015-06-10 18:46:04 -04:00
if ( msg . ffz _old _messages ) {
2015-11-19 02:45:56 -05:00
var max _messages = buffer _size - ( messages . length + 1 ) ;
if ( max _messages <= 0 )
msg . ffz _old _messages = null ;
else if ( msg . ffz _old _messages . length > max _messages )
msg . ffz _old _messages = msg . ffz _old _messages . slice ( msg . ffz _old _messages . length - max _messages ) ;
2015-06-10 18:46:04 -04:00
}
2015-11-19 02:45:56 -05:00
if ( ! first _inserted )
first _inserted = msg ;
2016-07-13 02:06:50 -04:00
// Store the message ID for this message, of course.
var msg _id = msg . tags && msg . tags . id ,
2016-09-09 17:34:20 -04:00
notice _type = msg . tags && msg . tags [ 'msg-id' ] ,
ids = r . ffz _ids = r . ffz _ids || { } ,
notices = r . ffz _last _notices = r . ffz _last _notices || { } ;
2016-07-13 02:06:50 -04:00
if ( msg _id && ! ids [ msg _id ] )
ids [ msg _id ] = msg ;
2016-09-09 17:34:20 -04:00
if ( notice _type && ! notices [ notice _type ] )
notices [ notice _type ] = msg ;
2015-06-10 18:46:04 -04:00
messages . unshiftObject ( msg ) ;
inserted += 1 ;
2015-11-19 02:45:56 -05:00
2015-06-10 18:46:04 -04:00
} else
2015-11-19 02:45:56 -05:00
return false ;
2015-06-10 18:46:04 -04:00
}
2015-11-07 22:56:15 -05:00
2015-11-19 02:45:56 -05:00
// If there's a CLEARCHAT, stop processing.
2015-11-07 22:56:15 -05:00
if ( msg . tags && msg . tags . target === '@@' )
2015-11-19 02:45:56 -05:00
return false ;
2015-11-07 22:56:15 -05:00
2015-11-19 02:45:56 -05:00
return true ;
} ) ;
2015-06-10 18:46:04 -04:00
2015-11-19 02:45:56 -05:00
if ( ! first _inserted )
return ;
var now = Date . now ( ) - ( first _inserted . from _server ? this . _ws _server _offset || 0 : 0 ) ,
age = now - first _inserted . date . getTime ( ) ;
if ( age > 300000 ) {
2015-06-10 18:46:04 -04:00
var msg = {
color : "#755000" ,
2015-11-19 02:45:56 -05:00
date : first _inserted . date ,
2015-06-10 18:46:04 -04:00
from : "frankerfacez_admin" ,
style : "admin" ,
2015-11-19 02:45:56 -05:00
message : "(Last message is " + utils . human _time ( age / 1000 ) + " old.)" ,
room : room _id ,
from _server : from _server
2015-06-10 18:46:04 -04:00
} ;
2015-11-19 02:45:56 -05:00
this . tokenize _chat _line ( msg , false , delete _links ) ;
2015-06-10 18:46:04 -04:00
if ( r . shouldShowMessage ( msg ) ) {
messages . insertAt ( inserted , msg ) ;
2015-11-19 02:45:56 -05:00
while ( messages . length > buffer _size ) {
2016-07-13 02:06:50 -04:00
// Remove this message from the ID tracker.
2016-09-09 17:34:20 -04:00
var m = messages . get ( 0 ) ,
msg _id = m . tags && m . tags . id ,
notice _type = m . tags && m . tags [ 'msg-id' ] ;
if ( msg _id && r . ffz _ids && r . ffz _ids [ msg _id ] )
delete r . ffz _ids [ msg _id ] ;
if ( notice _type && r . ffz _last _notices && r . ffz _last _notices [ notice _type ] === m )
delete r . ffz _last _notices [ notice _type ] ;
2016-07-13 02:06:50 -04:00
2015-06-10 18:46:04 -04:00
messages . removeAt ( 0 ) ;
2015-11-14 23:52:49 -05:00
removed ++ ;
}
2015-06-10 18:46:04 -04:00
}
}
2016-10-05 01:31:10 -04:00
} * /
2015-06-10 18:46:04 -04:00
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 )
if ( key !== 'room' && data . hasOwnProperty ( key ) )
model [ key ] = data [ key ] ;
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-10-05 01:31:10 -04:00
if ( model . css || model . moderator _badge )
utils . update _css ( this . _room _style , room _id , 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 : [ ] ,
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-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-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 ) ;
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' ) ,
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.
if ( ( 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 ) )
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-03-23 19:28:22 -04:00
} . observes ( 'r9k' , 'subsOnly' , 'emoteOnly' , 'slow' , 'ffz_banned' ) ,
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 ) ;
2015-06-05 03:59:28 -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-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 ) {
var last _notice = this . ffz _last _notices && this . ffz _last _notices [ target _notice ] ;
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 ) {
// 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
// f.log("Login Moderation", event);
2016-09-09 17:34:20 -04:00
// In case we get unexpected input, do the other thing.
if ( [ "ban" , "unban" , "timeout" ] . indexOf ( event . moderation _action ) === - 1 )
return this . _super ( event ) ;
var tags = {
'ban-duration' : event . moderation _action === 'unban' ? - Infinity : event . args [ 1 ] ,
'ban-reason' : event . args [ 2 ] ,
'ban-moderator' : event . created _by
} ;
2016-09-30 13:09:03 -04:00
this . clearMessages ( event . args [ 0 ] . toLowerCase ( ) , tags , false , event . moderation _action !== 'unban' ) ;
2016-09-09 17:34:20 -04:00
} ,
clearMessages : function ( user , tags , disable _log , report _only ) {
2015-06-10 18:46:04 -04:00
var t = this ;
2016-05-13 23:56:59 -04:00
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-07-13 02:06:50 -04:00
msg _id = undefined ,
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' ] ;
// Does anything really matter?
if ( ! report _only && duration !== - Infinity ) {
2016-05-13 23:56:59 -04:00
2016-07-13 02:06:50 -04:00
// Is there a UUID on the end of the ban reason?
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 = 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-06-05 22:12:54 -04:00
if ( typeof duration === "number" && duration && isFinite ( duration ) && ! isNaN ( duration ) )
2016-05-13 23:56:59 -04:00
t . updateWait ( duration )
else if ( duration ) {
t . set ( 'slowWait' , 0 ) ;
f . _roomv && f . _roomv . ffzUpdateStatus ( ) ;
}
}
// Mark the user as recently banned.
if ( ! t . ffzRecentlyBanned )
t . ffzRecentlyBanned = [ ] ;
t . ffzRecentlyBanned . push ( user ) ;
while ( t . ffzRecentlyBanned . length > 100 )
t . ffzRecentlyBanned . shift ( ) ;
2015-08-09 23:32:29 -07:00
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-09-09 17:34:20 -04:00
// End of report_only check.
}
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
// Now we need to see about displaying a ban notice.
if ( ! disable _log ) {
// Look up the user's last ban.
var show _notice = is _me || this . ffzShouldDisplayNotice ( ) ,
show _reason = is _me || this . get ( 'isModeratorOrHigher' ) ,
2016-09-30 13:09:03 -04:00
show _moderator = f . settings . get _twitch ( 'showModerationActions' ) ,
2016-05-20 17:30:34 -04:00
room = f . rooms && f . rooms [ t . get ( 'id' ) ] ,
now = new Date ,
end _time = now + ( duration * 1000 ) ,
ban _history , last _ban ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
if ( room ) {
ban _history = room . ban _history = room . ban _history || { } ;
last _ban = ban _history [ user ] ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
// Only overwrite a ban in the last 15 seconds.
if ( ! last _ban || Math . abs ( now - last _ban . date ) > 15000 )
last _ban = null ;
}
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
// Display a notice in chat.
2016-09-09 17:34:20 -04:00
var message = ( is _me ?
"You have" : ffz . format _display _name ( FFZ . get _capitalization ( user ) , user , true , false , true ) [ 0 ] + " has" ) +
" been " + ( duration === - Infinity ? 'unbanned' :
( duration === 1 ? 'purged' :
( isFinite ( duration ) ? "timed out for " + utils . duration _string ( duration , true ) : "banned" ) ) ) ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
if ( show _notice ) {
if ( ! last _ban ) {
var msg = {
style : "admin" ,
date : now ,
ffz _ban _target : user ,
reasons : reason ? [ reason ] : [ ] ,
2016-09-09 17:34:20 -04:00
moderators : moderator ? [ moderator ] : [ ] ,
2016-07-13 02:06:50 -04:00
msg _ids : msg _id ? [ msg _id ] : [ ] ,
2016-05-20 17:30:34 -04:00
durations : [ duration ] ,
end _time : end _time ,
2016-09-09 17:34:20 -04:00
timeouts : report _only ? 0 : 1 ,
2016-09-30 13:09:03 -04:00
message : message + ( show _reason && show _moderator && moderator ? ' by ' + moderator : '' ) + ( show _reason && reason ? ' with reason: ' + reason : '.' )
2016-05-20 17:30:34 -04:00
} ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
if ( ban _history )
ban _history [ user ] = msg ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
this . addMessage ( msg ) ;
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
} else {
2016-07-13 02:06:50 -04:00
if ( msg _id && last _ban . msg _ids . indexOf ( msg _id ) === - 1 )
last _ban . msg _ids . push ( msg _id ) ;
2016-05-20 17:30:34 -04:00
if ( reason && last _ban . reasons . indexOf ( reason ) === - 1 )
last _ban . reasons . push ( reason ) ;
2016-09-09 17:34:20 -04:00
if ( moderator && last _ban . moderators . indexOf ( moderator ) === - 1 )
last _ban . moderators . push ( moderator ) ;
2016-05-20 17:30:34 -04:00
if ( last _ban . durations . indexOf ( duration ) === - 1 )
last _ban . durations . push ( duration ) ;
last _ban . end _time = end _time ;
2016-09-09 17:34:20 -04:00
if ( ! report _only )
last _ban . timeouts ++ ;
last _ban . message = message +
( last _ban . timeouts > 1 ? ' (' + utils . number _commas ( last _ban . timeouts ) + ' times)' : '' ) +
2016-09-30 13:09:03 -04:00
( ! show _reason || ! show _moderator || last _ban . moderators . length === 0 ? '' : ' by ' + last _ban . moderators . join ( ', ' ) ) +
2016-09-09 17:34:20 -04:00
( ! show _reason || last _ban . reasons . length === 0 ? '.' : ' with reason' + utils . pluralize ( last _ban . reasons . length ) + ': ' + last _ban . reasons . join ( ', ' ) ) ;
2016-05-20 17:30:34 -04:00
last _ban . cachedTokens = [ { type : "text" , text : last _ban . message } ] ;
// Now that we've reset the tokens, if there's a line for this,
if ( last _ban . _line )
2016-06-02 20:04:40 -04:00
Ember . propertyDidChange ( last _ban . _line , 'ffzTokenizedMessage' ) ;
2016-05-20 17:30:34 -04:00
}
}
// Mod Card History
if ( room && f . settings . mod _card _history ) {
var chat _history = room . user _history = room . user _history || { } ,
user _history = room . user _history [ user ] = room . user _history [ user ] || [ ] ,
last _ban = user _history . length > 0 ? user _history [ user _history . length - 1 ] : null ;
if ( ! last _ban || ! last _ban . is _delete || Math . abs ( now - last _ban . date ) > 15000 )
last _ban = null ;
2016-05-22 14:08:11 -04:00
if ( last _ban ) {
2016-07-13 02:06:50 -04:00
if ( msg _id && last _ban . msg _ids . indexOf ( msg _id ) === - 1 )
last _ban . msg _ids . push ( msg _id ) ;
2016-05-22 14:08:11 -04:00
if ( reason && last _ban . reasons . indexOf ( reason ) === - 1 )
last _ban . reasons . push ( reason ) ;
if ( last _ban . durations . indexOf ( duration ) === - 1 )
last _ban . durations . push ( duration ) ;
last _ban . end _time = end _time ;
last _ban . timeouts ++ ;
last _ban . cachedTokens = [ message + ' (' + utils . number _commas ( last _ban . timeouts ) + ' times)' + ( last _ban . reasons . length === 0 ? '.' : ' with reason' + utils . pluralize ( last _ban . reasons . length ) + ': ' + last _ban . reasons . join ( ', ' ) ) ] ;
} else {
2016-05-20 17:30:34 -04:00
user _history . push ( {
from : 'jtv' ,
is _delete : true ,
style : 'admin' ,
date : now ,
ffz _ban _target : user ,
reasons : reason ? [ reason ] : [ ] ,
2016-07-13 02:06:50 -04:00
msg _ids : msg _id ? [ msg _id ] : [ ] ,
2016-05-20 17:30:34 -04:00
durations : [ duration ] ,
end _time : end _time ,
timeouts : 1 ,
cachedTokens : message + ( reason ? ' with reason: ' + reason : '.' )
} )
while ( user _history . length > 20 )
user _history . shift ( ) ;
}
2015-07-04 17:06:36 -04:00
}
}
2016-05-13 23:56:59 -04:00
2016-05-20 17:30:34 -04:00
2015-06-10 18:46:04 -04:00
} else {
if ( f . settings . prevent _clear )
this . addTmiMessage ( "A moderator's attempt to clear chat was ignored." ) ;
else {
var msgs = t . get ( "messages" ) ;
t . set ( "messages" , [ ] ) ;
t . addMessage ( {
style : 'admin' ,
2016-10-05 01:31:10 -04:00
message : i18n ( "Chat was cleared by a moderator" )
//ffz_old_messages: msgs
2015-06-10 18:46:04 -04:00
} ) ;
}
}
} ,
2016-09-30 13:09:03 -04:00
/ * t r i m M e s s a g e s : f u n c t i o n ( ) {
2015-07-29 01:03:10 -04:00
var messages = this . get ( "messages" ) ,
len = messages . get ( "length" ) ,
limit = this . get ( "messageBufferSize" ) ;
2015-08-14 13:21:57 -04:00
2016-07-13 02:06:50 -04:00
if ( len > limit ) {
var to _remove = len - limit ;
for ( var i = 0 ; i < to _remove ; i ++ ) {
// Remove this message from the ID tracker.
2016-09-09 17:34:20 -04:00
var msg = messages . get ( 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-07-13 02:06:50 -04:00
}
messages . removeAt ( 0 , to _remove ) ;
}
2016-09-30 13:09:03 -04:00
} , * /
2015-07-29 01:03:10 -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 = [ ] ,
new _unread = 0 ;
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" ) {
if ( msg . ffz _has _mention )
this . ffz _last _mention = Date . now ( ) ;
new _unread ++ ;
}
}
}
if ( ! new _messages . length )
return ;
var room _messages = this . get ( "messages" ) ,
trimmed = room _messages . length + new _messages . length > this . messageBufferSize ?
room _messages . slice ( Math . max ( 0 , ( room _messages . length - this . messageBufferSize ) ) + new _messages . length , room _messages . length ) :
room _messages . slice ( 0 , room _messages . length ) ;
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
}
} ,
2016-09-30 13:09:03 -04:00
/ * f f z A c t u a l P u s h M e s s a g e : f u n c t i o n ( m s g ) {
2015-08-09 23:32:29 -07:00
if ( this . shouldShowMessage ( msg ) && this . ffzShouldShowMessage ( msg ) ) {
2015-07-29 01:03:10 -04:00
this . get ( "messages" ) . pushObject ( msg ) ;
this . trimMessages ( ) ;
2016-09-30 13:09:03 -04:00
Ember . propertyDidChange ( this , "messages" ) ;
2015-06-10 18:46:04 -04:00
2015-12-12 13:28:35 -05:00
if ( msg . style !== "admin" && msg . style !== "whisper" ) {
if ( msg . ffz _has _mention ) {
this . ffz _last _mention = Date . now ( ) ;
}
this . ffz _last _activity = Date . now ( ) ;
this . incrementProperty ( "unreadCount" , 1 ) ;
}
2015-06-10 18:46:04 -04:00
}
2016-09-30 13:09:03 -04:00
} , * /
2015-06-10 18:46:04 -04: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' ) ) ;
} ,
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' ] ,
is _resub = notice _type === 'resub' ,
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 ) {
this . addMessage ( {
style : "notification" ,
from : "twitchnotify" ,
date : msg . date || new Date ,
room : room _id ,
message : msg . tags [ 'system-msg' ]
} ) ;
// 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 ) ;
}
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 = { } ;
msg . tags . badges . subscriber = '1' ;
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 ) ;
if ( ! msg )
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-06-02 20:04:40 -04:00
2016-09-30 13:09:03 -04:00
// Keep the history.
if ( ! is _whisper && msg . from && msg . from !== 'jtv' && msg . from !== 'twitchnotify' && f . settings . mod _card _history ) {
var room = f . rooms && f . rooms [ msg . room ] ;
if ( room ) {
var chat _history = room . user _history = room . user _history || { } ,
user _history = room . user _history [ msg . from ] = room . user _history [ msg . from ] || [ ] ,
last _history = user _history . length && user _history [ user _history . length - 1 ] ,
new _msg = {
from : msg . from ,
tags : {
'display-name' : msg . tags && msg . tags [ 'display-name' ] ,
bits : msg . tags && msg . tags . bits
} ,
message : msg . message ,
cachedTokens : msg . cachedTokens ,
style : msg . style ,
date : msg . date
} ;
if ( msg . tags && msg . tags . historical ) {
// If it's historical, insert it at the beginning. And stuff.
if ( user _history . length < 20 )
user _history . unshift ( new _msg ) ;
} else {
2016-06-02 20:04:40 -04:00
// Preserve message order if we *just* received a ban.
if ( last _history && last _history . is _delete && ( msg . date - last _history . date ) <= 200 ) {
user _history . splice ( user _history . length - 1 , 0 , new _msg ) ;
} else
user _history . push ( new _msg ) ;
2015-08-14 13:21:57 -04:00
2015-07-29 01:03:10 -04:00
if ( user _history . length > 20 )
user _history . shift ( ) ;
2016-09-30 13:09:03 -04:00
}
2015-11-07 22:56:15 -05:00
2016-09-30 13:09:03 -04:00
if ( f . _mod _card && f . _mod _card . ffz _room _id === msg . room && f . _mod _card . get ( 'cardInfo.user.id' ) === msg . from ) {
var el = f . _mod _card . get ( 'element' ) ,
2016-10-05 01:31:10 -04:00
history = el && el . querySelector ( '.chat-history.live-history' ) ;
2016-09-30 13:09:03 -04:00
if ( history ) {
2016-10-05 01:31:10 -04:00
var was _at _top = history . scrollTop >= ( history . scrollHeight - history . clientHeight ) ,
el = f . _build _mod _card _history ( msg , f . _mod _card ) ;
2016-09-30 13:09:03 -04:00
if ( msg . tags && msg . tags . historical )
history . insertBefore ( el , history . firstElementChild ) ;
else
history . appendChild ( el ) ;
if ( was _at _top )
setTimeout ( function ( ) { history . scrollTop = history . scrollHeight ; } )
// Don't do infinite scrollback.
if ( history . childElementCount > 100 )
history . removeChild ( history . firstElementChild ) ;
2015-11-07 22:56:15 -05:00
}
2015-07-04 17:06:36 -04:00
}
2015-07-29 01:03:10 -04:00
}
2016-09-30 13:09:03 -04:00
}
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-05-06 02:23:12 -04:00
// Message Filtering
var i = f . _chat _filters . length ;
while ( i -- )
if ( f . _chat _filters [ i ] ( msg ) === false )
return ;
2016-09-30 13:09:03 -04:00
// Also update chatters.
if ( ! is _whisper && this . chatters && ! this . chatters [ msg . from ] && msg . from !== 'twitchnotify' && msg . from !== 'jtv' )
this . ffzUpdateChatters ( msg . from ) ;
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.
if ( window !== window . parent && parent . postMessage && msg . from && msg . from !== "jtv" && msg . from !== "twitchnotify" )
parent . postMessage ( { 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
} ,
2016-05-06 02:23:12 -04:00
ffzChatFilters : function ( msg ) {
var i = f . _chat _filters . length ;
} ,
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 ( ) ) ;
}
if ( this . get ( "experiments.shouldSeeRedesign" ) ) {
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 ) ;
}
} else channel . setHostMode ( {
target : target , delay : delay
} ) ;
}
2015-01-20 20:25:26 -05:00
} ,
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 {
var alias = f . _command _aliases [ cmd ] ,
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 ( ) {
if ( ! this . tmiRoom )
return ;
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 ) {
var chatters = { } ;
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 ++ )
chatters [ data . admins [ i ] ] = true ;
if ( data && data . global _mods )
for ( var i = 0 ; i < data . global _mods . length ; i ++ )
chatters [ data . global _mods [ i ] ] = true ;
if ( data && data . moderators )
for ( var i = 0 ; i < data . moderators . length ; i ++ )
chatters [ data . moderators [ i ] ] = true ;
if ( data && data . staff )
for ( var i = 0 ; i < data . staff . length ; i ++ )
chatters [ data . staff [ i ] ] = true ;
if ( data && data . viewers )
for ( var i = 0 ; i < data . viewers . length ; i ++ )
chatters [ data . viewers [ i ] ] = true ;
2015-06-05 03:59:28 -04:00
room . set ( "ffz_chatters" , chatters ) ;
room . ffzUpdateChatters ( ) ;
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 ) {
var chatters = this . get ( "ffz_chatters" ) || { } ;
if ( add )
chatters [ add ] = true ;
if ( remove && chatters [ remove ] )
delete chatters [ remove ] ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( ! f . settings . chatter _count )
return ;
2015-01-20 01:53:18 -05:00
2015-06-05 03:59:28 -04:00
if ( f . _cindex )
f . _cindex . ffzUpdateChatters ( ) ;
2015-01-20 01:53:18 -05:00
2016-07-13 02:31:26 -04:00
if ( window !== window . parent && parent . postMessage )
parent . postMessage ( { from _ffz : true , command : 'chatter_count' , data : Object . keys ( 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
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
}